I have the following members of a class (v1 & v2) that I need to serialize to file using boost serialization and then read back in at a later stage.
The first one is a vector of a vector of an open cv type Point3f, and the other is a vector of a vector of type opencv Vec6f as can be seen below.
#include this in the class header
#include <boost/serialization/vector.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
//private member variables
std::vector< std::vector<cv::Point3f> > v1;
std::vector< std::vector<cv::Vec6f> > v2;
// Allow serialization to access non-public data members.
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & v1;
ar & v2;
}
I have this defined at the top of my header file so that it can understand how to serialize the point3f and Vec6f types. Is this correct way? I can save the data but when I go to read it all back in, it has the right number of array of vectors but each entry has values of 0. What am I doing wrong? I presume it's do do with the serialize functions themselves but not sure.
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive &ar, cv::Point3_<float> pt3, const unsigned int)
{
ar & pt3.x;
ar & pt3.y;
ar & pt3.z;
}
template<class Archive>
void serialize(Archive &ar, cv::Vec<float,6> vec6, const unsigned int)
{
ar & vec6[0];
ar & vec6[1];
ar & vec6[2];
ar & vec6[3];
ar & vec6[4];
ar & vec6[5];
}
}
}
I use this code to write
std::ofstream ofs1("v1test.bin");
boost::archive::binary_oarchive oa1(ofs1);
oa1 << v1;
and I use this code to read
std::ifstream ifs1("v1test.bin");
boost::archive::binary_iarchive ia1(ifs1);
std::vector< std::vector<cv::Point3f> > test;
ia1 >> test; // this contains the right structure but all values are 0
Any help or points in the right direction would be appreciated
I figured it out - stupid mistake - forgot to add a reference to the variables when passing them in - should be this
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive &ar, cv::Point3_<float> &pt3, const unsigned int)
{
ar & pt3.x;
ar & pt3.y;
ar & pt3.z;
}
template<class Archive>
void serialize(Archive &ar, cv::Vec<float,6> &vec6, const unsigned int)
{
ar & vec6[0];
ar & vec6[1];
ar & vec6[2];
ar & vec6[3];
ar & vec6[4];
ar & vec6[5];
}
}
}
Related
I'm using Boost 1.59.0 to serialize objects (let's call them Foo) in a std::forward_list<Foo>. However, I am unable to do this in cases where std::forward_list<Foo> is empty. To demonstrate my problem I have extended the "Very simple case" from Boost's serialization tutorial.
#include <fstream>
#include <unordered_map>
#include <forward_list>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/forward_list.hpp>
class Foo
{
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int)
{
ar & a;
ar & b;
}
int a;
int b;
public:
Foo()
: a(-1), b(-1) {}
Foo(int new_a, int new_b)
: a(new_a), b(new_b) {}
};
typedef std::forward_list<Foo> TListType;
class gps_position
{
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int)
{
ar & degrees;
ar & minutes;
ar & seconds;
ar & my_list;
}
int degrees;
int minutes;
float seconds;
TListType my_list;
public:
gps_position(){};
gps_position(int d, int m, float s, TListType new_list) :
degrees(d), minutes(m), seconds(s), my_list(new_list)
{}
};
int main()
{
std::ofstream ofs("filename");
TListType my_list(0);
// my_list.push_front(Foo(-1,-1));
const gps_position g(35, 59, 24.567f, my_list);
{
// save data
boost::archive::text_oarchive oa(ofs);
oa << g;
}
gps_position newg;
{
std::ifstream ifs("filename");
boost::archive::text_iarchive ia(ifs);
ia >> newg;
}
return 0;
}
This code compiles with g++ 4.8.3, however running it throws:
terminate called after throwing an instance of
'boost::archive::archive_exception'
what(): input stream error
Aborted (core dumped)
If we add one element to the list by uncommenting this line
// my_list.push_front(Foo(-1,-1));
no exception is thrown and everything seems to work. If I replace std::forward_list<Foo> with std::forward_list<int> in the above code then the code also works. Additionally, using std::list<Foo> or std::vector<Foo> also works, so the problem seems to be exclusive to std::forward_list.
Question: How can I serialize empty std::forward_list<Foo> without inserting dummy Foo objects?
Try closing the ofstream before reading from the file. Best way, move ofs inside the "// save data" block:
{
// save data
std::ofstream ofs("filename");
boost::archive::text_oarchive oa(ofs);
oa << g;
}
My guess is, with no data the write is smaller than the ofstream buffer and the file is physically empty/incomplete at the time of reading.
This is a flaw in the "very simple case" example, if you ask me.
This is caused by a bug in boost (still there in boost 1.67, explanation and patch here: https://svn.boost.org/trac10/ticket/13563). It's triggered by certain combinations of compiler and default constructors in the list elements.
As a workaround you can serialize a flag indicating if the list is empty. You need to split the serialize function into 'save' and 'load', e.g.:
template<typename Archive>
void save(Archive& ar, const unsigned int version) const
{
ar << degrees;
ar << minutes;
ar << seconds;
bool empty = nodes_.empty(); // workaround
ar << empty;
if (!empty)
ar << nodes_;
}
template<typename Archive>
void load(Archive& ar, const unsigned int version)
{
ar >> degrees;
ar >> minutes;
ar >> seconds;
bool empty; // workaround
ar >> empty;
if (!empty)
ar >> my_list;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
I am trying to serialize a class member. The following snippet of code will show the relevant class definitions and the non-intrusive serialization code I have included. I am getting a segfault during my saveHashTable() method which tries to serialize the class member shash_table_. Here is a self-contained trimmed version of the code:
#include <map>
#include <string>
#include <vector>
#include <set>
#include <fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/list.hpp>
using namespace std;
struct ORROctree
{
public:
struct Node
{
public:
struct Data
{
public:
float n_[3], p_[3];
int id_x_, id_y_, id_z_, lin_id_, num_points_;
std::set<Node*> neighbors_;
void *user_data_;
};
public:
Node::Data *data_;
float center_[3], bounds_[6], radius_;
Node *parent_, *children_;
};
protected:
float voxel_size_, bounds_[6];
int tree_levels_;
Node* root_;
std::vector<Node*> full_leaves_;
};
struct ModelLibrary
{
template <typename T, typename REAL = float>
struct NDIMVoxelStructure
{
T *voxels_;
std::vector<int> total_num_of_voxels_i_;
std::vector<int> num_of_voxels_;
long total_num_of_voxels_;
std::vector<REAL> bounds_;
std::vector<REAL> spacing_;
std::vector<REAL> min_center_;
};
typedef std::pair<const ORROctree::Node::Data*, const ORROctree::Node::Data*> Dipole;
struct Base {
Dipole seg1;
Dipole seg2;
};
typedef std::list<Base> bases_list;
typedef std::map <string, bases_list> SerializeHashTableCell;
// MEMBER TO BE SERIALIZED
typedef NDIMVoxelStructure<SerializeHashTableCell> SerializeHashTable;
public:
SerializeHashTable shash_table_;
public:
bool saveHashTable();
bool loadHashTable();
};
// SERIALIZATION METHODS FOR THE TYPES USED TO FORM THE SERIALIZEHASHTABLE
namespace boost {
namespace serialization {
template<class Archive>
inline void serialize(Archive & ar, ModelLibrary::SerializeHashTable & h, const unsigned int version)
{
ar & h.total_num_of_voxels_;
ar & boost::serialization::make_array(h.voxels_, h.total_num_of_voxels_);
ar & h.num_of_voxels_;
ar & h.total_num_of_voxels_i_;
ar & h.bounds_;
ar & h.spacing_;
ar & h.min_center_;
}
template<class Archive>
inline void serialize(Archive & ar, ModelLibrary::Base & b, const unsigned int version)
{
ar & b.seg1;
ar & b.seg2;
}
template<class Archive>
inline void serialize(Archive & ar, ORROctree::Node n, const unsigned int version)
{
ar & n.data_;
ar & n.center_;
ar & n.bounds_;
ar & n.radius_;
ar & n.parent_;
ar & n.children_;
}
template<class Archive>
inline void serialize(Archive & ar, ORROctree::Node::Data d, const unsigned int version)
{
ar & d.id_x_;
ar & d.id_y_;
ar & d.id_z_;
ar & d.neighbors_;
ar & d.lin_id_;
ar & d.num_points_;
ar & d.p_;
}
}
}
bool ModelLibrary::saveHashTable ()
{
std::ofstream ofs("test.txt");
boost::archive::text_oarchive oa(ofs);
oa << shash_table_;
return true;
}
bool
ModelLibrary::loadHashTable ()
{
std::ifstream ifs("test.txt");
boost::archive::text_iarchive ia(ifs);
ia >> shash_table_;
return true;
}
int main()
{
ModelLibrary m;
m.saveHashTable();
}
You just need to initialize the data in your data structures. All primitive types that haven't been explicitely initialized will have indeterminate ("random") values. This includes the pointers, which means you're invoking Undefined Behaviour by dereferencing pointers to random memory locations.
Here's an update with the simplest initialization that could possibly work, and it runs clean under Valgrind. That's a good tool. Use it!
Also, crank up the compiler messages (-Wall -Wextra -pedantic at a minimum for gcc/clang).
Added in various places:
Data() : id_x_(0), id_y_(0), id_z_(0), lin_id_(0), num_points_(0), user_data_(0)
{
std::fill(n_, n_+3, 0);
std::fill(p_, p_+3, 0);
}
Node() : data_(0), radius_(0), parent_(0), children_(0)
{
std::fill(center_, center_+3, 0);
std::fill(bounds_, bounds_+6, 0);
}
ORROctree() : voxel_size_(0), tree_levels_(0), root_(0)
{
std::fill(bounds_, bounds_+6, 0);
}
NDIMVoxelStructure() : voxels_(0), total_num_of_voxels_(0)
{ }
Base() : seg1(0, 0), seg2(0, 0)
{ }
See it Live On Coliru
Update From the comment: the following lines were clearly in error:
inline void serialize(Archive & ar, ORROctree::Node n, const unsigned int version)
inline void serialize(Archive & ar, ORROctree::Node::Data d, const unsigned int version)
And should have read
inline void serialize(Archive & ar, ORROctree::Node& n, const unsigned int version)
inline void serialize(Archive & ar, ORROctree::Node::Data& d, const unsigned int version)
Again I have issue while serializing shared pointer using boost and below is code :
//Content.hpp file
#include <boost/serialization/string.hpp>
#include <boost\serialization\shared_ptr.hpp>
#include <boost/serialization/list.hpp>
struct Content
{
std::string type;
boost::shared_ptr<Content> mycontent; // mycontent is of type Content
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & id;
ar & name;
ar & mycontent;
}
public:
Content(void);
Content(const parameter_strings & parms);
~Content(void);
};
// Content.cpp file
Content::Content(void)
{
}
Content::~Content(void)
{
}
Content::Content(const parameter_strings & parms)
{
// implementation part
}
if I comment line " -- boost::shared_ptr mycontent; --" it compiles without error but i need to use shared_ptr and hence it gives error :
it gives error : error C4308: negative integral constant converted to unsigned type
I have included all required header files also but still issue exists.
I already answered this in the comments here:
#user3382670 making the destructor virtual enabled RTTI for your class. This means that typeid(variable) will return the proper runtime type (most derived class) insetad of the statically known type with pointers and references. – sehe Mar 22 at 1:01
Also, since you don't require polymorphism there should be no such problem in the first place: see it Live On Coliru
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct Content
{
std::string id, name;
boost::shared_ptr<Content> mycontent;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int /*version*/)
{
ar & id;
ar & name;
ar & mycontent;
}
public:
Content() {}
typedef int parameter_strings;
Content(const parameter_strings & parms) { }
~Content() {}
};
int main()
{
boost::archive::text_oarchive ao(std::cout);
Content x;
ao << x;
}
I have 3 classes ("Leader", "Researchers", "Workers") which all derive from a base-class "Team".
I have a class "Project" which contains a vector of pointers to different Teams.
I use all of the following headers, in this order, in all of my class declarations:
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_member.hpp>
To (de)serialize the Team object I use:
private:
friend class boost::serialization::access ;
template <typename Archive>
void serialize(Archive& ar, const unsigned int /*version*/)
{
ar & teamname ;
}
To (de)serialize the Leader, Researchers, Workers objects I use:
typedef Team _super;
friend class boost::serialization::access ;
template <typename Archive>
void serialize(Archive& ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<_super>(*this) ;
ar & contactTelephoneNumber ;
}
The Project holds a std::vector of pointers to different teams and a string using:
std::vector<Team *> teams ;
std::string note ;
I use the following code in the Project class for serialization:
private:
friend class boost::serialization::access ;
template <typename Archive>
void serialize(Archive& ar, const unsigned int /*version*/)
{
//ar & BOOST_SERIALIZATION_NVP(teams) ; //ERROR OCCURS HERE
ar & teams;
ar & note ;
}
And to serialize the vector of Project objects in the main I use:
{
std::ostringstream archiveStream ;
boost::archive::text_oarchive archive(archiveStream) ;
archive << BOOST_SERIALIZATION_NVP(projects) ;
//Get serialized info as string
archivedProjects = archiveStream.str() ;
}
This all compiles fine. The issue is at Run-Time. When the above section of the code is reached, I get the following error:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what():
unregistered class - derevided class not registered or exported"
The program goes as far as:
ar & teams;
In the Questionnaire class's serialization attempt.
As in n.m.'s link: you need to register the classes with Boost so that it knows what classes are what when serialising.
You need to add the following line for each class where "Project" serializes:
ar.template register_type<ClassName>() ; //ClassName = Team etc
The serialization example below is from the boost mailing list which is pretty much the same as what I would like to do. However, I have changed the archive so that it will serialize to XML. The compile does not fail if I serialize to binary, but it fails when serializing to xml. The compile fails in basic_xml_oarchive.hpp in the following method:
// boost code where compile fails
template<class T>
void save_override(T & t, BOOST_PFTO int)
{
// If your program fails to compile here, its most likely due to
// not specifying an nvp wrapper around the variable to
// be serialized.
BOOST_MPL_ASSERT((serialization::is_wrapper<T>));
this->detail_common_oarchive::save_override(t, 0);
}
It seems I haven't done enough to allow the std::map<int, CSomeData> object to be serialized, any ideas on how to fix this?
My serialization implementation:
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/map.hpp>
#include <fstream>
#include <string>
#include <map>
using namespace std;
// This is a test class to use as the map data.
class CSomeData {
public:
CSomeData(){};
CSomeData(float f0, string str0)
{
m_f0 = f0;
m_str0 = str0;
}
float m_f0;
string m_str0;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & m_f0;
ar & m_str0;
}
};
// This is the class we really want to try serializing.
class CTest {
public:
CTest(){};
CTest(int nNumber)
{
m_nNumber = nNumber;
// Fill with some dummy data.
m_mTst.insert(make_pair(0, CSomeData(0.23f, "hi hi hi")));
m_mTst.insert(make_pair(1, CSomeData(7.65f, "second one")));
m_mTst.insert(make_pair(2, CSomeData(9.23f, "third one")));
m_mTst.insert(make_pair(3, CSomeData(5.6766, "chosen one")));
}
~CTest(){};
save()
{
std::ofstream ofs("filename");
// Write class instance to archive. Writing seems to work ok.
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(*this);
}
int m_nNumber;
private:
map<int, CSomeData> m_mTst;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & m_nNumber;
ar & m_mTst;
}
};
I believe you need to tag the members with a name for XML serialisation. This specifies the element name to use in the XML. I.e. use something like:
ar & BOOST_SERIALIZATION_NVP(m_f0);
or (better in this case):
ar & make_nvp("field0", my_f0);
The tags will be ignored for binary serialisation. More details here:
http://www.boost.org/doc/libs/1_43_0/libs/serialization/doc/wrappers.html