boost::serialization: overloading load_construct_data: no accessible constructor at all - c++

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;
}

Related

Capture handle by reference or by value to class template ctor

Consider a tempalted class InputBuffer:
template<class Source, size_t Capacity>
class InputBuffer
{
public:
explicit InputBuffer(Source src);
int getchar();
private:
std::byte const* m_read_ptr;
std::byte const* m_last_valid;
Source m_src;
std::array<std::byte, Capacity> m_data;
void fetchAndResetPointers();
};
Question: Should the constructor instead accept src as a reference and store a pointer instead of taking it by value? It is very likely that the caller expect reference semantics here. However, it is also possible that Source already is some kind of pointer, and then, taking src by reference and storing a pointer would lead to an unnecessary indirection. If not passing by reference, the user can use std::ref if needed.
This class is no good as is in my opinion - you assume that "source" requires an m_read_ptr, m_last_valid and m_data as context. However, if it's a file, for instance, it requires non of these. Instead, rewrite this class as an interface, or better yet, don't create a generic class at all and use templates when handling "sources", example in pseudo-code:
class FileBuffer {
public:
explicit FileBuffer(File* f) : m_f(f) {}
int getchar() { return read(f, 1); }
private:
File* m_f;
};
template<class T>
void print_from_buffer_to_stdout(T& buf) {
std:: cout << buf.getchar();
}
int main() {
FileBuffer f = get_file_buffer(); // somehow
print_from_buffer_to_stdout(f);
}

Is it possible to declare a class member both const/non-const?

I have a class that can take both a non-const pointer or a const pointer as arguments to its overloaded constructors. In my particular case, I need to instantiate an object of this class from both const and non-const methods of class T, but it fails from const methods, as it can't assign the const pointer to foo .
myClass() {
public:
myClass(T* v);
myClass(const T* v);
// ...
T* foo;
// ...
}
Is it possible to assign the argument in both constructors to foo? If so, what would be the correct syntax?
EDIT:
In a more specific case, I have a class myClass that wraps around std::vector and allows to me to directly access subsets of a vector through a nested class mySubset:
template<typename _type>
myClass() {
std::vector<_type> data;
public:
class mySubset(){
myClass<type>* foo;
public:
mySubset(myClass<_type>* _in) { foo = _in; };
mySubset(const myClass<_type>* _in) { foo = _in; /* error */ };
// ...
}
// ...
myClass();
// ...
void mySubset method() { return mySubset(this); };;
void mySubset const_method const() { return mySubset(this); /* error */ };
// ...
}
The code within is irrelevant -basically mySubset allows to both read and write to specific vector positions. While I'm able to achieve what I want with separate const and non-const nested classes, I was looking for a way to do this with a single return type.
I think you'll have to reconsider your design since you can't initialize a T* with a const T* lvalue, without const_cast which should be avoided unless you're really really sure, (since it invokes an undefined behavior if you try to modify a const pointer after casting away its constness)
Instead, you could use template type deduction for const and non const
template <typename T>
class myClass {
public:
//myClass(T* v):foo(v) { }
myClass( T* v):foo(v)
{
}
// ...
T* foo;
// ...
};
Then,
int a =42;
const int* p1 = &a;
int *p2 = &a;
myClass X1(p1); //C++17 auto type deduction or use myClass<const int> X1(p1)
myClass X2(p2);
You could using const_cast in your const T* constructor, but typically you shouldn't.
const T* means "point to a constant value T", and you store a "pointer to a T". If you do a const cast, you could end up modifying a value which shouldn't be modified. If you aren't going to modify foo, just declare it const T* and just use the single const T* constructor.
I'd check to see if this is a design issue. A lot of the times these scenarios appear:
(1) Where you're storing a pointer to something as non-const when it should be const. Typically this is because you're accessing values in another object and you should be passing the object as a (possibly const) reference at each use site rather than storing a pointer to it.
(2) When you really want to store a copy of an object, in which case you just keep a regular T and pass it in as const T& in the constructor.
(3) You're dealing with raw C-style strings and want to copy the contents into your own buffer.
If you don't want to use parameterized-type (template) class as #P0W's answer, it is not possible you can use only one pointer to accept all constant and non-constant pointer type. You need another constant pointer type to accept only const <your another class> * in your wrapper class.
Below code works after you have two separate pointer types in wrapper class which you may not like.
#include <iostream>
using namespace std;
class SomeObject {
public:
SomeObject(){}
explicit SomeObject(int i):testVal(i){}
private:
int testVal;
};
class PtWrapper {
public:
PtWrapper(SomeObject *pso);
PtWrapper(const SomeObject *cpso);
private:
SomeObject *pSO;
const SomeObject *cpSO;
};
int main(int argc, char *argv[]) {
SomeObject so(133);
SomeObject *pso = &so;
const SomeObject cso(166);
const SomeObject *cpso = &cso;
PtWrapper pw1(pso);
PtWrapper pw2(cpso);
return 0;
}
PtWrapper::PtWrapper(SomeObject *pso) :pSO(pso){
}
PtWrapper::PtWrapper(const SomeObject *cpso):cpSO(cpso){}

Cereal: deserialize a vector of objects without default constructor

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

C++, adding temporary objects to a list, without dynamic memory allocation

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

c++ Segmentation fault in destructor child class

hi i am delevop a multicontainner using templates, but i am getting a segmentation fault from the child class destructor, here is the code:
#include <algorithm>
#include <map>
#include <iostream>
class BaseType{
public:
virtual ~BaseType(){}
virtual BaseType * clone() const =0;
};
template<typename T>
class DataType : public BaseType
{
public:
DataType(const T & aValueData = T()):mValue(aValueData) {
// new DataType<T>(*this)
}
~DataType(){
}
BaseType * clone() const
{
return new DataType<T>(*this);
}
T mValue;
};
class MValueData
{
public:
template<typename T>
MValueData(T const & aAnyValue = T()):mTypeData(0),isDelete(false)
{
std::cout<<"Object Address before create object: "<<mTypeData<<std::endl;
mTypeData=new DataType<T>(aAnyValue);
std::cout<<"Object Address after create object"<<mTypeData<<std::endl;
}
~MValueData(){
std::cout<<"Object Address "<<mTypeData<<std::endl;
delete mTypeData;
mTypeData=0;
}
MValueData()
{
mTypeData=0;
}
template<typename T>
MValueData(const MValueData & aCopy)
{
mTypeData= new DataType<T>();
*mTypeData=aCopy.mTypeData;
}
template<typename T>
const MValueData & operator=(const MValueData & aCopy)
{
mTypeData= new DataType<T>();
*mTypeData=aCopy.mTypeData;
//MValueData(aCopia).swap(*this);
}
void swap(MValueData& other) {
std::swap(this->mTypeData, other.mTypeData);
}
template <typename T>
T& get()
{
return dynamic_cast<DataType<T>&>(*this->mTypeData).mValue;
}
bool operator <(const MValueData &rhs) const {
return (mTypeData<rhs.mTypeData);
}
template<typename T>
void setValue(T const & anyValue=T())
{
mTypeData= new DataType<T>(anyValue);
}
BaseType *mTypeData;
private:
bool isDelete;
};
int main()
{
MValueData aAnyType_1(0.22);
aAnyType_1.get<double>();
MValueData aAnyType_2(false);
std::map<MValueData , MValueData&> mMapa;
mMapa.insert(std::pair<MValueData , MValueData&>(aAnyType_1,aAnyType_2));
// mMapa.find(aAnyType_1);
return 0;
}
I am using GDB to determinate the bug but i cannot see proper way to fix, the segmentacion stop when i comment this line:
~MValueData(){
// if(mTypeData) delete mTypeData;
}
Only then it run propperly, but it seems that i am creating a memory leak.
Updated:std::map create copys of the object that i insert into, the object is destroyed twice, one when exit the main function and another when std::map is destroying it self,
any hint?
thx in advance!
This segmentation fault might appear to be in the destructor, but it is a problem in your copy constructor. Lets take a simple example, I have a class which stores a pointer. Then I copy this pointer value like you are doing: I will have two pointers to the same memory location. Now I delete one of these objects, thus deleting the value at the pointer. The second object will have a pointer to invalid memory, and when this tries to delete the memory you get a segmentation fault.
How to fix this:
Well there are a few ways actually. Firstly, you need to decide whether you want deep copying of the pointers or not. If you do, write a deep copy of the memory the pointer points too. If not I reocmmend using shared_ptr to avoid these sorts of problems.
Your copy constructor is broken. You are not cloning the object, but only the pointer. Once an object is copied, the two copies will try to delete the same real object in memory in the destructor and that will cause a crash. You need to figure out who should own the object and whether you want it cloned or shared among the different instances of the MValueData object. Then act accordingly to fix the issue.
Your copy constructor is not correct.
It only copies the pointer not the object.
The two objects copied with the copy constructor will try to delete the same object in their destructor.