Closed
Description
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();
}