Skip to content

Another shared_from_this bug... This time, invalidated shared_from_this state after SAVING (not loading) #68

Closed
@Devacor

Description

@Devacor

This bug literally took me a month to write a minimal test case for. I assumed I was doing something bad, but it seems cereal's archive molests the shared_from_this state without calling a destructor!

See the line with: //BUSTED

#include <strstream>

#include "cereal/cereal.hpp"
#include "cereal/types/map.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/memory.hpp"
#include "cereal/types/string.hpp"
#include "cereal/types/base_class.hpp"

#include "cereal/archives/json.hpp"
#include "cereal/types/polymorphic.hpp"

#include <memory>

struct Node : public std::enable_shared_from_this<Node> {
    Node(int val):data1(val){ std::cout << "Base Constructor" << std::endl; }
    virtual ~Node(){ std::cout << "Base Destructor" << std::endl; }

    template <class Archive>
    void serialize(Archive & archive){
        archive(CEREAL_NVP(data1), CEREAL_NVP(child));
    }

    template <class Archive>
    static void load_and_allocate(Archive & archive, cereal::allocate<Node> &allocate){
        int data1;
        archive(cereal::make_nvp("data1", data1));
        allocate(data1);
        archive(cereal::make_nvp("child", allocate->child));
    }

    std::shared_ptr<Node> child;

    int data1 = 0;
};

struct DerivedNode : public Node {
    DerivedNode(int val):Node(val), data2(val){ std::cout << "Derived Constructor" << std::endl; }
    ~DerivedNode(){ std::cout << "Derived Destructor" << std::endl; }

    template <class Archive>
    void serialize(Archive & archive){
        archive(CEREAL_NVP(data2), cereal::make_nvp("base", cereal::base_class<Node>(this)));
    }

    template <class Archive>
    static void load_and_allocate(Archive & archive, cereal::allocate<DerivedNode> &allocate){
        allocate(-1);
        archive(cereal::make_nvp("data2", allocate->data2), cereal::make_nvp("base", cereal::base_class<Node>(allocate.ptr())));
    }

    int data2 = 2;
};

CEREAL_REGISTER_TYPE(DerivedNode);

void saveTest(){
    int testInt = 10;
    int *pointInt = &testInt;
    std::stringstream stream;
    {
        std::shared_ptr<Node> node = std::make_shared<Node>(1);
        node->child = std::make_shared<DerivedNode>(3); //if this is just "Node" it's fine again!
        auto test1 = node->child->shared_from_this(); //Fine

        cereal::JSONOutputArchive archive(stream);
        auto test2 = node->child->shared_from_this(); //Fine

        auto nvp = cereal::make_nvp("node", node);
        auto test3 = node->child->shared_from_this(); //Fine

        archive(nvp);
        auto test4 = node->child->shared_from_this(); //BUSTED
        std::cout << "Completed Save!" << std::endl;
    }
    std::cout << stream.str() << std::endl;
    std::cout << std::endl;
    {
        std::shared_ptr<Node> node;

        cereal::JSONInputArchive archive(stream);
        archive(cereal::make_nvp("node", node));

        node->shared_from_this();
        std::cout << "We good?" << node->data1 << std::endl;

        node->child->shared_from_this();
        std::cout << "We still good?" << node->child->data1 << std::endl;
    }
    std::cout << "Done" << std::endl;
}

int main(){
    saveTest();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions