I want to serialize/deserialize using BOOST the values (not the pointers) of objects in the following vector:
std :: vector <A*> m_vector;
To serialize I use the following code:
int nItems = m_vector.size();
ar & nItems;
std::for_each(m_vector.begin(), m_vector.end(), [&ar](A* pItem) {
ar & *pItem;
});
And to deserialize:
int nItems;
ar & nItems;
for (int i = 0; i < nItems; ++i) {
A* pItem;
ar & *pItem; ///////////// Run-Time Check Failure #3
m_vector.push_back(pItem);
}
But when I run the program I get the following error:
Run-Time Check Failure # 3 - The variable 'pItem' is Being Used without Being initialized.
What am I doing wrong?
Thank you.
You will need to allocate memory for the object pointed to by pItem:
A* pItem = new A;
ar & *pItem;
m_vector.push_back(pItem);
The error was because although you had a pointer, there was no object at the memory location where the pointer pointed to -- the value of the pointer was garbage (uninitialized pointer).
Don't forget to call delete when you no longer need the object pointed to by the pointer in the vector to preven memory leak. Better yet, use a smart pointer (e.g. boost::shared_ptr<>) to ensure the memory is deallocated when no longer accessible.
2 years later, but worth mentioning.
There's been a better solution to serialize a vector of pointers to objects or any other STL container (list, set etc.).
In order to serialize a vector, add:
#include <boost/serialization/vector.hpp>
and then you need to implement serialize() method and friend your class with archieve.
Everything is explained in this example (read all comments carefully, they're very important):
#include <fstream>
#include <iostream>
#include <vector>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
class Abc{
// In order to make Abc serializable
// you need to friend this lass with serialization::access
friend class boost::serialization::access;
// and then add this method
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// choose what class fields do you want to serialize
ar & a;
ar & b;
ar & c;
}
public:
int a;
int b;
int c;
// don't forget about default constructor! It's necessary for serialization!
Abc(){};
Abc(int a, int b, int c): a(a), b(b), c(c){};
};
class GpsPosition
{
private:
// as mentioned above...
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
ar & wektorIntow;
}
int degrees;
int minutes;
float seconds;
public:
std::vector<Abc*> abcVector;
GpsPosition(){};
GpsPosition(int d, int m, float s): degrees(d), minutes(m), seconds(s)
{
// adding some objects to abcVector
abcVector.push_back(new Abc(1, 2, 3));
abcVector.push_back(new Abc(3, 2, 3));
abcVector.push_back(new Abc(2, 2, 3));
abcVector.push_back(new Abc(1, 2, 3));
}
int getDegrees(){ return this->degrees; }
int getMinutes(){ return this->minutes; }
float getSeconds(){ return this->seconds; }
};
int main(){
// And now how to use it
// Saving to file:
std::ofstream fileHandler("filename");
const GpsPosition position1(35, 59, 24.567f);
{
boost::archive::text_oarchive boostOutputArchieve(fileHandler);
boostOutputArchieve << position1;
}
// Reading from file:
GpsPosition newPosition;
{
std::ifstream fileHandler;
try{
fileHandler.open("filenacme");
boost::archive::text_iarchive boostInputArchieve(fileHandler);
// read class state from archive
boostInputArchieve >> newPosition;
// archive and stream closed when destructors are called
fileHandler.close();
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening/reading/closing file";
}
catch(boost::archive::archive_exception e){
std::cerr << "Exception opening/reading/closing file";
}
}
// print to the console
std::cout << newPosition.getMinutes() << std::endl;
std::cout << newPosition.abcVector[0]->a;
std::cin.get();
return 0;
}
For more information, check out this tutorial:
http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html
Related
In my project I have a class containing and std::list and in another class I maintain an iterator pointing to a place in the middle of that list.
I can successfully serialize the list, but the iterator member variable is causing problems. Here is a program to reproduce:
#include <boost/serialization/list.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <fstream>
class A
{
public:
A(){}
A(const std::list<int> & _i) : i(_i) {}
virtual ~A(){}
std::list<int> i;
void display() {
std::cout << "i:";
for (const int j : i)
std::cout << " " << j;
std::cout << std::endl;
}
private:
friend class boost::serialization::access;
//friend std::ostream & operator<<(std::ostream &os, const A &a);
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & i;
}
};
class Stepper
{
public:
Stepper() {}
Stepper(const A& a)
: p(a.i.size()>0 ? a.i.begin() : a.i.end()) {}
std::list<int>::const_iterator p;
void display() {
std::cout << "p: " << *p << std::endl;
}
void step() { p++; }
private:
friend class boost::serialization::access;
//friend std::ostream & operator<<(std::ostream &os, const A &a);
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
};
int main()
{
{
A a({5,6,7});
Stepper sa(a);
a.display();
sa.display();
sa.step();
sa.display();
std::ofstream ofs( "a.txt" );
boost::archive::text_oarchive ar(ofs);
ar & a;
ar & sa;
}
A b;
Stepper sb;
{
std::ifstream ifs( "a.txt" );
boost::archive::text_iarchive ar(ifs);
ar & b;
ar & sb;
}
b.display();
sb.display();
return 0;
}
In this program, the class A can be serialized without problems. (Remove the ar&sa stuff..) But unfortunately when trying to serialize the class containing the iterator (the exact code above), I get the following compilation errors:
[..snip..]
testser.cpp:72:10: required from here /usr/include/boost/serialization/access.hpp:116:11:
error: ‘struct std::_List_const_iterator<int>’ has no member named ‘serialize’
t.serialize(ar, file_version);
~~^~~~~~~~~
[..snip..]
testser.cpp:81:10: required from here /usr/include/boost/serialization/access.hpp:116:11:
error: ‘struct std::_List_const_iterator<int>’ has no member named ‘serialize’
So, it seems that boost/serialization/list.hpp does not support iterators. And yet, as far as I can tell, it's totally legitimate to keep an iterator to a list item somewhere, as they cannot be invalidated unless erased. Is there a way to serialize this iterator using boost? Do I need to write a custom function? Do I have to return a custom iterator from my std::list? (That sounds particularly ugly..)
Thanks for any insight.
Okay it seems the only way to do this is to split the serialization into save and load, and calculate the iterator's position in the list. This works as long as the iterator is valid. Unfortunately it means needing to add a pointer to the list to the structure, which I didn't want, but actually in my application I can access this so it is not a problem for me.
class Stepper
{
public:
Stepper() {}
Stepper(const A& _a)
: a(&_a), p(a->i.size()>0 ? a->i.begin() : a->i.end()) {}
const A* a;
std::list<int>::const_iterator p;
void display() {
std::cout << "p: " << *p << std::endl;
}
void step() { p++; }
private:
friend class boost::serialization::access;
template<class Archive>
void save(Archive &ar, const unsigned int version) const
{
int d = std::distance(a->i.begin(), p);
ar & a;
ar & d;
}
template<class Archive>
void load(Archive &ar, const unsigned int version)
{
int d;
ar & a;
ar & d;
p = a->i.begin();
for (; d>0; --d)
p++;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
I am working on a simple serialization class. I keep throwing an exception on the input stream. I have put together the below example of what I am attempting to accomplish in simple terms.
I have this simple example of boost serialization that I am getting an exception on:
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#define NVP(X) X
class base {
public:
friend class boost::serialization::access;
base (){ v1 = 10;}
int v1;
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version)
{
ar & NVP(v1);
}
virtual void bla()=0;
};
class derived : public base {
public:
friend class boost::serialization::access;
int v2 ;
derived() { v2 = 100;}
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){
boost::serialization::base_object<base>(* this);
ar & NVP(v2);
}
virtual void bla(){};
};
BOOST_CLASS_EXPORT(base);
BOOST_CLASS_EXPORT_GUID(derived, "derived");
int main ( )
{
std::stringstream ss;
boost::archive::text_oarchive ar(ss);
base *b = new derived();
ar << NVP(b);
std::cout << ss.str()<<std::endl;
std::istringstream ssi;
base *b1 = new derived();
{
boost::archive::text_iarchive ar1(ssi);
ar1 >> b1;
}
//std::cout << ssi.str();
std::cout << "v1: " << b1->v1 << std::endl;
}
The exception that I am getting is:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): input stream error
Any help would be appreciated.
You're reading from an empty stream:
std::istringstream ssi;
// ...
boost::archive::text_iarchive ar1(ssi);
Also, you leak this object:
base *b1 = new derived();
Here's a fixed example, notes:
it's very good practice/important to close archives before using the streamed data
BOOST_CLASS_EXPORT_GUID(derived, "derived") doesn't add anything beyond BOOST_CLASS_EXPORT(derived)
you can print the v2 conditionally:
if (auto* d = dynamic_cast<derived*>(b1))
std::cout << "v2: " << d->v2 << std::endl;
I've used bla() as an example to print the values instead
NVP() is a bit iffy there. Why not just leave it out for non-tagged archives (ie. other than XML)? If you intend to support XML, just use BOOST_SERIALIZATION_NVP, boost::serialization::make_nvp etc.
std::cout << "v2: " << b1->v2 << std::endl; was completely out of place
just initialize b1 to null so you don't leak it; remember to free all pointers (use smart pointers!)
the mix of public: and friend in your types didn't really mean much
Live On Coliru
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <sstream>
class base {
public:
base(int v1) : v1(v1) {}
virtual void bla() const = 0;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned /*int const file_version*/) {
ar & BOOST_SERIALIZATION_NVP(v1);
}
protected:
int v1;
};
class derived : public base {
public:
derived(int v1 = 10, int v2 = 100) : base(v1), v2(v2) {}
virtual void bla() const {
std::cout << "v1: " << v1 << ", v2: " << v2 << "\n";
}
private:
friend class boost::serialization::access;
int v2;
template <class Archive> void serialize(Archive &ar, unsigned /*int const file_version*/) {
boost::serialization::base_object<base>(*this);
ar & BOOST_SERIALIZATION_NVP(v2);
}
};
BOOST_CLASS_EXPORT(base)
BOOST_CLASS_EXPORT(derived)
int main() {
std::stringstream ss;
{
boost::archive::text_oarchive ar(ss);
base *b = new derived();
ar << boost::serialization::make_nvp("base", b);
delete b; // TODO use RAII instead
}
std::cout << ss.str() << std::endl;
base *deserialized = nullptr;
{
boost::archive::text_iarchive ar1(ss);
ar1 >> boost::serialization::make_nvp("base", deserialized);
}
deserialized->bla();
delete deserialized;
}
Prints
22 serialization::archive 12 0 7 derived 1 0
0 100
v1: 10, v2: 100
I am trying to use boost's functionality for serializing pointers to primitives (so that I don't have to de-reference and do a deep store myself). However, I get a pile of errors when I try to do it. Here is a simple example of a class that is supposed to contain save and load methods which write and read the class content from a file. This program does not compile:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
class A
{
public:
boost::shared_ptr<int> sp;
int const * p;
int const& get() {return *p;}
void A::Save(char * const filename);
static A * const Load(char * const filename);
//////////////////////////////////
// Boost Serialization:
//
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar,const unsigned int file_version)
{
ar & p & v;
}
};
// save the world to a file:
void A::Save(char * const filename)
{
// create and open a character archive for output
std::ofstream ofs(filename);
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
// write the pointer to file
oa << this;
}
}
// load world from file
A * const A::Load(char * const filename)
{
A * a;
// create and open an archive for input
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
// read class pointer from archive
ia >> a;
return a;
}
int main()
{
}
Note that I am not interested in a solution that dereferences the pointer; I want boost to take care of that for me (many of these classes might be pointing to the same underlying object).
In http://www.boost.org/doc/libs/1_54_0/libs/serialization/doc/index.html:
By default, data types designated primitive by Implementation Level
class serialization trait are never tracked. If it is desired to track
a shared primitive object through a pointer (e.g. a long used as a
reference count), It should be wrapped in a class/struct so that it is
an identifiable type. The alternative of changing the implementation
level of a long would affect all longs serialized in the whole program
- probably not what one would intend.
Hence:
struct Wrapped {
int value;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar,const unsigned int file_version)
{
ar & value;
}
};
boost::shared_ptr<Wrapped> sp;
Wrapped const * p;
Is it possible to construct objects from directly from the archive?
Something like this...
// Non-working pseudo code
struct Foo {
BOOST_SERIALIZATION_SPLIT_MEMBER();
std::vector<int> data;
Foo() {
// populate "data" by doing calculation
data.push_back(1); data.push_back(2);
}
template<class Archive>
Foo( Archive & ar ) {
// populate "data" by rading the archive
}
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
// Normal serialization of data
ar << data;
}
};
int main(int argc, const char *argv[])
{
// deserialize
boost::archive::text_iarchive oar(std::cin);
Foo foo(oar);
return 0;
}
You can use a deserializing constructor:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
class Point
{
public:
Point() = default;
Point(boost::archive::text_iarchive& archive)
{
archive >> *this;
}
float x = 1.;
float y = 2.;
private:
friend class boost::serialization::access;
template<class TArchive>
void serialize(TArchive & archive, const unsigned int version)
{
archive & x;
archive & y;
}
};
int main()
{
Point p;
p.x = 5;
p.y = 6;
std::ofstream outputStream("test.archive");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << p;
outputStream.close();
std::ifstream inputStream("test.archive");
boost::archive::text_iarchive inputArchive(inputStream);
Point pointRead(inputArchive);
std::cout << pointRead.x << " " << pointRead.y << std::endl;
return 0;
}
As I said in the comment.
Yes there is no problem with constructing from an archive.
(Another alternative is to have static load function but that can have performance penalties).
The only potential problem I see with your approach is that your constructor can take almost anything as an argument and that can create problems.
And that can interfere with the copy constructor and other single argument constructors relying in implicit conversion.
So one has to restrict to take archives only.
There are different methods to do this, but based in this conversation http://marc.info/?l=boost&m=121131260728308&w=2, and by the fact that the inheritance tree of the archives is documented http://www.boost.org/doc/libs/1_35_0/libs/serialization/doc/class_diagram.html, I think this is the best solution is to check that the argument derives from basic_iarchive.
#include<type_traits>
struct Foo {
...
std::vector<int> data;
Foo() {
// populate "data" by doing calculation
data.push_back(1); data.push_back(2);
}
template<class IArchive,
typename = std::enable_if_t<std::is_base_of<boost::archive::detail::basic_iarchive, IArchive>::value>>
Foo( IArchive & ar ) {
ar >> data;
// populate "data" by reading the archive
}
...
};
int main(int argc, const char *argv[])
{
// deserialize
boost::archive::text_iarchive iar(std::cin);
Foo foo(iar); // will also work with other archives
}
As for what happens when your data is not default constructive see the discussion above.
See below a main() and two very simple classes. Then per Boost serialization (and what is shown) my questions are:
1) Does class B need the normal overloaded stream insertion operators '<<' and '>>' to be defined? Currently in my real code it doesn't have these.
2) Does class A in the store() and load() methods need to iterate through the map and multimap containers explicitely, storing/loading their key:value pairs explicitely?
e.g. something like:
void A::store(const char* filename){
std::ofstream ofs(filename);
boost::archive::text_oarchive oa(ofs);
std::map< std::string, B >::iterator it;
BMap.size();
oa << BMap.size();
for( it = BMap.begin(); it != BMap.end(); it++ ){
oa << it->first;
oa << it->second;
}
//similar for strMultimap
}
I assume that I don't need to do this, but am not certain.
3) Assuming class B has only the two data members shown, does it need a default contructor included explicitely? (as opposed to the implicit default constructor)
4) Does B need to have an overide for the comparison operator '>'? I assume that it doesn't since this is a very simple class.
Finally, any other comments per anything that I've failed to cover is appreciated!
Example code for my above questions:
//includes ommitted
int main() {
std::string file("test.dat");
A * pA = new A;
pA->store(file.c_str());
pA->fillMaps();
//release data
pA->load(file.c_str());
return 0;
}
//includes ommitted
class A
{
friend class boost::serialization::access;
public:
std::map< std::string, B > BMap;
std::multimap< std::string, std::string > strMultimap;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & BMap;
ar & strMultimap;
}
void store(const char* filename){
std::ofstream ofs(filename);
boost::archive::text_oarchive oa(ofs);
oa << this;
}
void load(const char* filename){
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
ia >> this;
}
void fillMaps(){
//code to allocate B objects and put them in BMap and fill strMultimap with whatever number of key:value pairs
}
class B
{
friend class boost::serialization::access;
public:
std::string str;
unsigned int num;
B::B(void)
: str("a string")
, num(7)
{
}
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & str;
ar & num;
}
}
1) You don't need stream operators for class B, but it does need a serialize() method. I had to wrap the serialization with the BOOST_SERIALIZATION_NVP (name value pair) macro:
ar & BOOST_SERIALIZATION_NVP(someNamedValue); // use this macro for everything you want to name
There might be a way to avoid naming your map, but I don't know how that's done.
2) No, class A doesn't need map-specific serialization code. Just make sure you include <boost/serialization/map.hpp>.
3) The implicit default constructor should be fine. You only ever need an explicit default constructor if a) you've already provided a non-default constructor or b) you want to change the behavior of the default constructor.
4) No operator < is needed :)
Here's some sample code which compiled, but I haven't run:
#include <boost/serialization/map.hpp>
struct A
{
struct B
{
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
typedef std::map<int, SomeClass> MyMap;
MyMap myMap;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(myMap);
}
};