I'm trying to use Cereal to serialize an object without default constructor. Storing such objects directly or via smart pointer works. However,
when I put the object into a container, it no longer compiles:
error: no matching function for call to ‘Node::Node()’
Is there a way to get cereal to store/restore vectors of objects without default constructor?
My test code:
#include <fstream>
#include <cereal/archives/json.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/vector.hpp>
class Node {
public:
Node(int* parent) {};
int value_1;
template<class Archive>
void serialize(Archive& archive) {
archive(
CEREAL_NVP(value_1)
);
}
template<class Archive>
static void load_and_construct(Archive& archive, cereal::construct<Node>& construct) {
construct(nullptr);
construct->serialize(archive);
}
};
int main() {
std::string file_path = "../data/nodes.json";
Node node_1{nullptr}; // this would serialize
std::vector<Node> nodes; // this does not
nodes.push_back(Node{nullptr});
nodes.push_back(Node{nullptr});
std::vector<std::unique_ptr<Node>> node_ptrs; // this would serialize
node_ptrs.push_back(std::make_unique<Node>(nullptr));
node_ptrs.push_back(std::make_unique<Node>(nullptr));
{ //store vector
std::ofstream out_file(file_path);
cereal::JSONOutputArchive out_archive(out_file);
out_archive(CEREAL_NVP(nodes));
}
{ // load vector
std::ifstream in_file(file_path);
cereal::JSONInputArchive in_archive(in_file);
in_archive(nodes);
}
return 0;
}
As far as I understand the way this library works, there is no way to deserialize something that doesn't have a default constructor at least for dynamically allocated objects.
The logic under this is the following:
You need to deserialize vector<Node>
In order to do that you need to allocate an appropriate amount of memory
cereal doesn't know about the constructor and can't properly allocate object memory by itself
In order to provide a proper object building it requires the default constructor
Related
To enable move semantics on a std::vector do I need to pass by value; so the compiler will not copy the element but just move them?
Example:
class data
{
public:
void setMyData(vector<string> sourceData)
{
private_data_ = sourceData;
}
private:
vector<string> private_data_;
};
to enable move semantics on a C++ stl vector
You have misunderstanding about the move semantic concept.
The std::vector itself is move constructable as it has the move constructor defined. You do not need to enable it explicitly, rather use it properly.
In the function
void setMyData(vector<string> sourceData) // copy 1
{
private_data_= sourceData; // copy 2
}
You are doing double copy. You need instead
void setMyData(vector<string> sourceData) // copy
{
private_data_= std::move(sourceData); // move
}
or you might want
void setMyData(vector<string>&& sourceData) // only accept the rvalue
{
private_data_= std::move(sourceData); // move
}
and for the second case, you call the function
setMyData(std::move(sourceData));
I would recommend either using 2 overload of that function or even use a generic one:
#include <vector>
#include <string>
class data
{
public:
void setMyData(const std::vector<std::string>& sourceData){private_data_=sourceData;}
void setMyData(std::vector<std::string>&& sourceData){private_data_= std::move(sourceData);}
template <typename T>
void genericsetMyData(T&& source) {private_data_ = std::forward<T>(source);}
private:
std::vector<std::string> private_data_;
};
int main() {
class data a,b,c,d;
std::vector<std::string> s1,s2; const std::vector<std::string> s3;
a.setMyData(s1);
b.setMyData(std::move(s2));
c.genericsetMyData(s1);
d.genericsetMyData((std::move(s1)));
d.genericsetMyData(s3);
}
The templated one is a bit more complex since it uses a so called fowarding reference.
I suggest the following:
#include <utility>
class data
{
public:
void setMyData(vector<string> sourceData)
{
private_data_ = std::move(sourceData);
}
private:
vector<string> private_data_;
};
And to init, you have two ways:
vector<string> myStrings{"hello", "i am", "stringies"};
data mydata;
// Type 1: Give ownership
mydata.setMyData(std::move(myStrings)); // not a single copy, but myStrings is empty
// Type 2: Copy
mydata.setMyData(myStrings); // Copy once only, and myStrings is still valid
The good thing about this technique is that you don't have to write several overloaded methods, you can choose which way you want to pass your parameter.
I'm writing code for an embedded platform, therefore I cannot use the normal new operator.
Now I want to add arbitrary objects to a list, just like this.
tp.add(DerivedA("David"));
tp.add(DerivedB("Max"));
tp.add(DerivedC("Thomas"));
For the reason of code duplication I don't want to write something like this:
DerivedA david("David");
tp.add(david);
...
A solution, but not very pretty style would be this:
tp.add(new (myalloc(sizeof(DerivedB))) DerivedB("John"));
// using placement-new works
Now I tried to add a temporary object, passed by pointer:
tp.add(&DerivedA("David"));
Theoretically this could work, but the compiler complains (with good reason) about passing a pointer to a temporary object (-fpermissive).
Is there a clean way of doing what I want to?
Here is a full example:
#include <iostream>
using namespace std;
class Base // base class
{
public:
Base();
int size;
char name[100];
};
class Derived:public Base
{
public:
Derived(char* name);
};
class ThirdParty
{
public:
void add(Base* obj);
void addTemp(Base* tempObj);
Base* list[10];
int index;
};
void* myalloc(int size){
void* p;
// ...
// allocate memory in a static memory pool
// ...
return p;
}
void memcpy(void* to, void* from, int size){
}
int main()
{
ThirdParty tp;
// The ugly style:
tp.add(new (myalloc(sizeof(Derived))) Derived("John")); // using placement-new works
// The beauty style (compiler complains here):
tp.addTemp(&Derived("David")); // create temporary object here, which is copied and added to the list
tp.addTemp(&Derived("Max"));
tp.addTemp(&Derived("Thomas"));
return 0;
}
Base::Base()
{
size = sizeof(Base);
}
Derived::Derived(char *name)
{
size = sizeof(Derived); // make size of this object available for a base-pointer
}
void ThirdParty::add(Base *obj)
{
list[index++] = obj;
}
void ThirdParty::addTemp(Base* tempObj)
{
Base* newObj = (Base*) myalloc(tempObj->size); // let third party allocate memory
memcpy(newObj, tempObj, tempObj->size); // copy the temporary object
list[index++] = newObj;
}
If you use C++11, you could write a forwarding function to do the work for you:
template <typename T, typename... Args>
T* make (Args&&... args) {
return new (myalloc(sizeof(T))) T { std::forward<Args>(args)... };
}
You'd then add an object to your list like so:
tp.add(make<Derived>("John"));
My preferred solution now is the following macro:
#define m(x) new (myalloc(sizeof(x))) x
now I can add a new object with this code:
tp.add(m(Derived("Isabella")));
Can you not just override new to use myalloc ? If you do notcwant to do this globally, you certainly can do it for Base
so I'm using the boost::serialization library, and I'm trying to overide how a class is constructed, since it has no default constructor. This is demonstrated here. To me it appears the function takes the class* t and then sets it to point to a newly constructed object. If i'm wrong, this is definately the source of my error.
However, the only way to construct my class is by using another classes create() function, meaning I need to stray from the code in the example (this is stated in the boost::serialization namespace): ::new(t)my_class(attribute);
I tried simply calling the create function and setting t equal to the returned pointer, but this doesn't seem to work because right after the load_construct_data function, and in the serialization function, the given myClass& is not the same as what I set 't' to.
How do I do whatever ::new(t) is doing so the object created using the create function follows through into the serialize/other functions?
The construct referred to in your question (new(t) my_class(attribute)) is called "placement new". The way it work is that
t must already point to an allocated region of memory (placement new doesn't do allocation by default)
An instance of my_class is constructed at that memory location.
However, since in your case you can't use constructors, then using any form of new is out of the question. But there is an alternative (sort of).
Since placement new pretty much just overwrites a chunk memory, we can use a regular function which does the same with an already constructed object. Such a function is memcpy:
void * memcpy ( void * destination, const void * source, size_t num );
All memcpy does is perform a byte-wise copy of num bytes from the memory pointed to by source to the memory pointed to by destination.
So let's say you started with this code in the load_construct_data
my_class obj = other_class::create();
Then we can use the memcpy function to "move" the value at obj into the memory reference by t:
memcpy((void*)t, (void*)(&obj), sizeof(obj));
While there are some details about how this works with your particular class, such as whether a bit-wise copy is "good enough", this is the best I've got with what you've asked. The one problem I see is if the destructor releases resources, than the copy will may become invalid.
To account for the possible problems with destruction, you can write your own deep copying function:
void deepCopy( my_class * destination, const my_class * source );
which you call instead of memcpy.
Note: please tell me if I went astray with anything here. I don't currently have a machine to test code on.
In the case of not having a default constructor, the use of load_construct_data and save_construct data actually permits the use of shared_ptr instances. In my view, there is no need to play around with memcpy and raw pointers. Thus you can just do something like the following
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <memory>
class MyClass {
public:
explicit MyClass(std::string const &str) : m_str(str) {}
MyClass() = delete;
std::string str() const
{
return m_str;
}
private:
std::string m_str;
};
namespace boost { namespace serialization {
template<class Archive>
void serialize(Archive &ar,
MyClass const & myClass,
unsigned int const)
{
auto str = myClass.str();
ar & str;
}
template<class Archive>
void save_construct_data(Archive &ar,
MyClass const * myClass,
unsigned int const)
{
auto str = myClass->str();
ar << str;
}
template<class Archive>
void load_construct_data(Archive &ar,
MyClass * myClass,
unsigned int const)
{
std::string archived;
ar >> archived;
::new(myClass)MyClass(MyClass(archived));
}
}
}
int main(int argc, const char * argv[]) {
std::shared_ptr<MyClass> myClass(new MyClass("hello!"));
std::stringstream os;
::boost::archive::text_oarchive oa(os);
oa << myClass;
std::shared_ptr<MyClass> myClassB;
std::stringstream is;
is.str(os.str());
::boost::archive::text_iarchive ia(is);
ia >> myClassB;
return 0;
}
How can I make a vector of linked lists?
For example I have a struct of properties (for the linked-list) defined as follows:
typedef struct property {
string info;
property* implication;
} property;
And then I have a class object:
class objects {
private:
vector<property> *traits;
public:
void giveProperty(property *x) {
traits.push_back(&x);
}
};
Where what I want to do conceptually is give an object certain properties, and each property has a series of implications (which is the linked list) which I can use later. But I am getting the error:
request for member 'push_back' in '((objects*)this)->objects::traits', which is of non-class type 'std::vector >*'
I am having trouble getting this to work. Sorry if this is unclear, if you have questions I will try clarifying myself.
in class objects, you have declared a pointer to a vector of properties, when really you want a vector of property pointers:
class objects {
private:
vector<property*> traits; // Note where * is
public:
void giveProperty(property *x) {
traits.push_back(x); // Note that x is already a pointer, no need to take its address
}
};
I'm not sure why you are using raw pointers. You say you want to have a linked list, but you don't implement that anywhere. Why not use the std::list container for your linked lists and lose the pointers altogether?
#include <string>
#include <vector>
#include <list>
using std::string;
using std::vector;
using std::list;
struct implication {
implication(string name) : name(name) {}
string name;
};
struct property {
property(string info) : info(info) {}
string info;
list<implication> implList;
};
class object {
private:
vector<property> traits;
public:
void giveProperty(const property& x) {
traits.push_back(x);
}
};
int f() {
object o;
property p1("p1");
property p2("p2");
implication i("implic");
p1.implList.push_back(i);
p1.implList.push_back(implication("other implic"));
o.giveProperty(p1);
o.giveProperty(property("p3"));
}
Change this
traits.push_back(&x);
to this:
traits->push_back(*x);
Or probably/possibly, you would want to change this:
vector<property> *traits;
to this:
vector<property*> traits;
I would go for the latter one!
Do you understand what pointers are?
vector<property*> *traits;
You're creating a pointer to a vector, not a vector. You would need to create one:
traits = new vector<property*>;
then access it as:
traits->push_back(x);
Change -
vector<property> *traits;
to
vector<property*> traits; // Needs to be the declaration.
Since you are trying to push_back addresses to the vector traits.
void giveProperty(property *x) {
traits.push_back(&x);
^ Error : With the above modifications made, traits can hold
elements of type property*. By doing &x, you are trying to do
push_back pointer's address which in that case vector should
hold elements of type property**. So, just remove the &
symbol before x while push_back because x is already of type
property*
}
As far as I understand, there is no serialization (boost::serialization, actually) support for boost::any placeholder.
Does someone know if there is a way to serialize a custom boost::any entity?
The problem here is obvious: boost::any uses template-based placeholders to store objects and typeid to check if boost::any_cast is appropriate.
So, there is a custom abstract superclass placeholder and custom template-based derived classes, which are created the following way:
template <T> custom_placeholder : public placeholder {
virtual std::type_info type() const { return typeid(T); }
virtual ...
};
Obviously, this brings some troubles when even thinking about serializing this stuff. Maybe someone knows some trick to make such kind of serialization (and of course, proper deserialization)?
Thank you
If you want to stick with boost::any i am not sure but you can write your own "boost::any". I'm using this code for proxy methods to pass the parameters.
#include <iostream>
#include <boost\smart_ptr\scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <sstream>
class my_placeholder
{
public:
virtual ~my_placeholder(){}
my_placeholder(){}
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
//ar & boost::serialization::base_object<bus_stop>(*this);
//ar & m_placeholder;
}
};
template<typename T>
class my_derivedplaceholder:
public my_placeholder
{
public:
my_derivedplaceholder()
{
}
my_derivedplaceholder(T &value)
{
m_value=value;
}
T m_value;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
ar & boost::serialization::base_object<my_placeholder>(*this);
ar & m_value;
}
};
BOOST_CLASS_EXPORT_GUID(my_derivedplaceholder<int>, "p<int>");
class my_any
{
public:
my_any()
{
}
template<typename T>
my_any(const T &value)
{
m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value)));
}
template<typename T>
void operator=(const T &value)
{
m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value)));
}
protected:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
//ar & boost::serialization::base_object<bus_stop>(*this);
ar & m_placeholder;
}
template<typename T>
friend T my_anycast(my_any &val);
boost::shared_ptr<my_placeholder> m_placeholder;
};
template<typename T>
T my_anycast(my_any &val)
{
boost::shared_ptr<my_derivedplaceholder<T>> concrete=boost::dynamic_pointer_cast<my_derivedplaceholder<T>>(val.m_placeholder);
if (concrete.get()==NULL)
throw std::invalid_argument("Not convertible");
return concrete->m_value;
}
void main()
{
my_any m=10;
int a=my_anycast<int>(m);
std::cout << a << std::endl;
std::stringstream ss,ss2;
boost::archive::text_oarchive oa(ss);
oa << m;
boost::archive::text_iarchive ia(ss);
my_any m2;
ia >> m2;
std::cout << my_anycast<int>(m2) << std::endl;
}
It is not possible at all, at least for arbitrary types. Note that maybe you could serialize using some tricky code (like finding the size of the elements contained in the any), but the any code relies on the compiler statically putting the any type_code and the proper types inside the placeholder. You surely cannot do that in deserialization in C++, as the type that you'd get from the deserialization is not known at compile time (as required by the newly formed boost::any).
The best solution is to build some kind of specialized any type for the exact types of elements you're going to serialize. Then, you can have special cases for the actual type of element being deserialized, but note that each element type serialization/deserialization has to be phisically written as static C++ code.
PD. Some others suggested using boost::variant as a representation of this specialized type holding the exact types you're going to serialize. You need a way of discerning the exact type on deserialization, though (maybe assigning identifiers to types in the variant).
Assuming you have to use boost::any and you cannot switch to variant, a map<type_info const*, string(*)(any)> based solution could get you done.
You have to initialize at runtime such a map with all the types you plan to use. Of course, you can use something along the lines of
template <typename T>
struct any_serializer
{
static string perform(any a)
{
T const& x = any_cast<T const&>(a);
stringstream out;
out << x;
return out.str();
}
};
and populate the map with addresses of any_serializer<T>::perform under the key &typeid(T). You can specialize the class any_serializer and use some (ugly) macros to populate the map.
More difficult is of course the deserialization. I haven't had a look at boost::lexical_cast for a while, perhaps it can provide some help. I am afraid that this is totally problem-dependant. However, you only need one function, which takes a string and returns one any. You may also want to prepend your output string with a custom type identifier.
There is no need to create new class. Try to use xany https://sourceforge.net/projects/extendableany/?source=directory
xany class allows to add new methods to any's existing functionality. By the way there is a example in documentation which does exactly what you want.