icu::UnicodeString with boost serialize - c++

I am trying to serialize an icu::UnicodeString with the boost serialization library but am having trouble.
The icu::UnicodeString does not have the required serialize function to serialize it. So I tried to create it, but I am not sure how to make these. Example code:
#include <map>
#include <sstream>
#include <fstream>
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <boost/serialization/map.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
namespace boost {
namespace serialization {
template<class Archive>
inline void save(
Archive & ar,
const icu_55::UnicodeString& str,
const unsigned int /* file_version */
){
}
template<class Archive>
inline void load(
Archive & ar,
icu_55::UnicodeString& str,
const unsigned int /* file_version */
){
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<class Archive>
inline void serialize(
Archive & ar,
icu_55::UnicodeString& str,
const unsigned int file_version
){
boost::serialization::split_free(ar, str, file_version);
}
} // serialization
} // namespace boost
int main()
{
std::map<icu::UnicodeString, int> map = {{"asssdasd",2}, {"qwe",1}, {"Zxc",55}};
std::stringstream ss;
boost::archive::text_oarchive oarch(ss);
oarch << map;
std::map<icu::UnicodeString, int> new_map;
boost::archive::text_iarchive iarch(ss);
iarch >> new_map;
std::cout << (map == new_map) << std::endl;
}
Compile with something like g++ -o new new.cpp -std=c++11 -lboost_serialization -licuuc
Currently the "save" and "load" functions are not implemented. I tried doing just the ar & str; statements that are used in the boost manuals, but I am getting a segmentation fault with that that I am also unable to fix.

I've never worked with LibICU directly, so probably someone can review this code.
However, from my experience using Boost Serialization I think this should be helpful:
template <class Archive>
inline void save(Archive &ar, icu_55::UnicodeString const &str, const unsigned int) {
auto sz = str.getCapacity();
auto len = str.length();
auto buf = str.getBuffer();
if (!buf) throw std::invalid_argument("str");
ar & sz & len & boost::serialization::make_array(buf, sz);
}
template <class Archive>
inline void load(Archive &ar, icu_55::UnicodeString &str, const unsigned int)
{
size_t sz, len;
ar & sz & len;
auto buf = str.getBuffer(sz);
if (!buf) throw std::invalid_argument("str");
try {
ar & boost::serialization::make_array(buf, sz);
}
catch(...) {
str.releaseBuffer(len);
throw;
}
str.releaseBuffer(len);
}
It works for the simple test case provided:
#include <fstream>
#include <map>
#include <sstream>
#include <iostream>
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/map.hpp>
namespace boost { namespace serialization {
template <class Archive>
inline void save(Archive &ar, icu_55::UnicodeString const &str, const unsigned int) {
auto sz = str.getCapacity();
auto len = str.length();
auto buf = str.getBuffer();
if (!buf) throw std::invalid_argument("str");
ar & sz & len & boost::serialization::make_array(buf, sz);
}
template <class Archive>
inline void load(Archive &ar, icu_55::UnicodeString &str, const unsigned int)
{
size_t sz, len;
ar & sz & len;
auto buf = str.getBuffer(sz);
if (!buf) throw std::invalid_argument("str");
try {
ar & boost::serialization::make_array(buf, sz);
}
catch(...) {
str.releaseBuffer(len);
throw;
}
str.releaseBuffer(len);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template <class Archive>
inline void serialize(Archive &ar, icu_55::UnicodeString &str, const unsigned int file_version) {
boost::serialization::split_free(ar, str, file_version);
}
} } // serialization // namespace boost
int main() {
std::map<icu::UnicodeString, int> const map = { { "asssdasd", 2 }, { "qwe", 1 }, { "Zxc", 55 } };
std::stringstream ss;
{
boost::archive::text_oarchive oarch(ss);
oarch << map;
}
{
std::map<icu::UnicodeString, int> new_map;
boost::archive::text_iarchive iarch(ss);
iarch >> new_map;
std::cout << (map == new_map) << std::endl;
}
}
Prints
1

Related

boost::serialization of static array / archive exception on restore

The following code compiles and seems to serialize properly (that is, the static is saved only once apparently). However, it has an 'input stream error' exception on restore:
#include <boost/serialization/tracking.hpp>
#include <boost/serialization/level.hpp>
#include <boost/serialization/array.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <iostream>
#include <fstream>
#include <array>
class SA {
std::array<char, 1024*1024> sbuf;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int ver) {
ar & boost::serialization::make_array(sbuf.data(), sbuf.size());
};
};
BOOST_CLASS_IMPLEMENTATION(SA, boost::serialization::object_serializable); // serialization_level
BOOST_CLASS_TRACKING(SA, boost::serialization::track_always); // tracking_level
class Foo {
char buf[1024];
inline static SA sxbuf;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int ver) {
ar & boost::serialization::make_array(buf, sizeof(buf));
ar & sxbuf;
};
};
class FooList {
std::array<Foo, 100> fool;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int ver) {
ar & boost::serialization::make_array(fool.data(), fool.size());
};
};
int
main()
{
const std::string filename{"foo.sav"};
FooList x;
std::ofstream out{filename, std::ios::binary};
boost::archive::binary_oarchive oa(out);
oa << x;
std::cout << "Saved\n";
std::ifstream ifs{filename};
boost::archive::binary_iarchive ia(ifs);
if (ifs.fail()) {
std::cerr << "couldn't open input file " << filename << "\n";
return 1;
}
ia >> x; // gives exception
std::cout << "Restored\n";
return 0;
}
The output is as follows:
Saved
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): input stream error
(I tried this on godbolt too, but -lboost_serialization didn't seem to work, so it wouldn't link properly... possibly my error there).
Any insight much appreciated...
This opens an archive before the writing end is finalized:
std::ofstream out{filename, std::ios::binary};
boost::archive::text_oarchive oa(out);
oa << x;
std::cout << "Saved\n";
std::ifstream ifs{filename};
Also ifs is missing the ios::binary flag.
Other than that, you can
drop make_array,
prefer std::array over T[] and
default the serialization level.
Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/array.hpp>
#include <array>
#include <fstream>
#include <iostream>
class SA {
std::array<char, 1024 * 1024> sbuf;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &ar, unsigned) { ar &sbuf; }
};
BOOST_CLASS_TRACKING(SA, boost::serialization::track_always) // tracking_level
class Foo {
std::array<char, 1024> buf;
inline static SA sxbuf;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &ar, unsigned) { ar &buf &sxbuf; }
};
class FooList {
std::array<Foo, 100> fool;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &ar, unsigned) { ar &fool; }
};
int main() {
const std::string filename{"foo.sav"};
{
std::ofstream out(filename, std::ios::binary);
boost::archive::binary_oarchive oa(out);
FooList x{};
oa << x;
std::cout << "Saved\n";
}
{
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive ia(ifs);
FooList x{};
ia >> x; // gives exception
std::cout << "Restored\n";
}
}
Prints
Saved
Restored

Boost missing some data during serialization process

In my setup i have vector of OpacityChannel pointer as a member of Container class.
if i create a varible of OpacityChannel and write it to the archive than everything is written as expected.
when i write Container class object and write it to archive than it misses the data for KeyframeFloat class which is a member of OpacityChannel class.
This is my main file if i change the line from ar & cont; to ar & opacityChannel than it writes the data as required.
I am not able to understand why it is missing data when i write Container class.
#include "pch.h"
#include <iostream>
#include<fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include "Container.h"
#include "KeyframeFloat.h"
#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT_GUID(SumOpacity_Channel, "SumOpacity_Channel")
BOOST_CLASS_EXPORT_GUID(SumKeyframeFloat, "SumKeyframeFloat")
int main()
{
const char* fileName = "saved.txt";
std::vector<int> vec;
Container cont;
SumOpacity_Channel opacityChannel;
SumKeyframeFloat key1, key2;
opacityChannel.AddKeyframe(key1);
opacityChannel.AddKeyframe(key2);
cont.AddChannel(&opacityChannel);
SumKeyframeFloat key1_Restored, key2_Restored;
{
// Create an output archive
std::ofstream ofs(fileName);
boost::archive::text_oarchive ar(ofs);
ar & cont; // KeyframeFloat data is not written.
// if i do ar & opacityChannel; than keyframeFloat data is written in archive
}
Container c_Restored ;
SumOpacity_Channel opacityChannel_Restored;
//load data
{
//create an input stream
std::ifstream ifs(fileName);
boost::archive::text_iarchive ar(ifs);
ar & c_Restored ;
}
do
{
std::cout << '\n' << "Press a key to continue...";
} while (std::cin.get() != '\n');
}
This is the container Class
#pragma once
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/split_member.hpp>
#include "SumChannel.h"
#include "SumOpacityChannel.h"
#include <boost/serialization/vector.hpp>
#include <memory>
#include <boost/serialization/export.hpp>
class Container
{
private:
std::vector< SumOpacity_Channel* > Channels;
public:
Container() {} ;
~Container()
{
if(Channels.size() > 0 )
for (int i = 0; i < Channels.size(); i++)
{
delete Channels[i];
}
}
Container(const Container& c)
{
if (Channels.size() > 0)
Channels.clear(); // clear any previous channels
for (int i = 0; i < c.Channels.size(); i++)
{
Channels.push_back(c.Channels[i]->Clone());
}
}
Container& operator=(const Container& c)
{
if (Channels.size() > 0)
Channels.clear(); // clear any previous channels
for (int i = 0; i < c.Channels.size(); i++)
{
Channels.push_back(c.Channels[i]->Clone());
}
return *this;
}
void AddChannel(SumOpacity_Channel* channel)
{
Channels.push_back(channel->Clone());
}
private:
friend class boost::serialization::access;
template <typename Archive>
void save(Archive& ar, const unsigned version) const {
ar & Channels;
}
template <typename Archive>
void load(Archive& ar, const unsigned version) {
ar & Channels;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
This is the keyframeFloat class.
#pragma once
#include "KeyframeBase.h"
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/serialization.hpp>
class SumKeyframeFloat
{
private:
float x;
public:
SumKeyframeFloat() { this->x = 0.0; };
SumKeyframeFloat(float xvalue) : x(xvalue) { };
SumKeyframeFloat(const SumKeyframeFloat& key) : x ( key.x) { };
~SumKeyframeFloat() = default;
private:
friend class boost::serialization::access;
template<typename Archive>
void save(Archive& ar, const unsigned int version) const {
std::cout << "writing float keyframe to Archive" << std::endl;
ar & x;
}
template<typename Archive>
void load(Archive& ar, const unsigned int version) {
ar & x;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
This is sum Opacity class header and defination
#include <string>
#include "SumChannel.h"
#include <iostream>
#include <vector>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/vector.hpp>
class SumOpacity_Channel
{
private:
std::vector<SumKeyframeFloat> keyframes;
public:
SumOpacity_Channel();
SumOpacity_Channel(const SumOpacity_Channel &opacityChannel);
~SumOpacity_Channel() = default;
SumOpacity_Channel& operator=(const SumOpacity_Channel &posChannel);
void AddKeyframe(SumKeyframeFloat key);
void DeleteKeyframe(SumKeyframeFloat key, int number);
SumOpacity_Channel* Clone() const;
public:
friend class boost::serialization::access;
template<typename Archive>
void save(Archive& ar, const unsigned int version) const {
ar & keyframes;
}
template<typename Archive>
void load(Archive& ar, const unsigned int version) {
ar & keyframes;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
////////////////////////////////////////////////
#include "pch.h"
#include "SumOpacityChannel.h"
SumOpacity_Channel::SumOpacity_Channel()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
SumOpacity_Channel::SumOpacity_Channel(const SumOpacity_Channel &opacityChannel)
{
this->keyframes.clear();
for (auto KeyFrame : keyframes)
{
this->keyframes.push_back(KeyFrame);
}
}
SumOpacity_Channel& SumOpacity_Channel::operator=(const SumOpacity_Channel &opacityChannel)
{
for (auto KeyFrame : keyframes)
{
this->keyframes.push_back(KeyFrame);
}
return *this;
}
void SumOpacity_Channel::AddKeyframe(SumKeyframeFloat key)
{
keyframes.push_back(key);
}
void SumOpacity_Channel::DeleteKeyframe(SumKeyframeFloat key, int number)
{
keyframes.erase(keyframes.begin() + number);
}
SumOpacity_Channel* SumOpacity_Channel::Clone() const
{
return new SumOpacity_Channel(*this);
}
Your channel is empty.
This copy constructor doesn't make any sense:
SumOpacity_Channel::SumOpacity_Channel(const SumOpacity_Channel &opacityChannel)
{
this->keyframes.clear(); // [1]
for (auto KeyFrame : keyframes) // [2]
{
this->keyframes.push_back(KeyFrame);
}
}
in [2] you are iterating over empty vector - it was cleared the line above.
Also why clear is called? It is constructor, so this vector could not be filled ever before. Calling clear makes sense in copy assignment operator, but not here.
So this
cont.AddChannel(&opacityChannel);
with that
void AddChannel(SumOpacity_Channel* channel)
{
Channels.push_back(channel->Clone());
}
SumOpacity_Channel* SumOpacity_Channel::Clone() const
{
return new SumOpacity_Channel(*this);
}
gives empty vector.
auto KeyFrame : keyframes
should be
auto KeyFrame : opacityChannel.keyframes
?

Boost Serialization throws exception while reading from binary

I have found this code and trying to modify it for binary object serialization on visual studios. But on doing so it throws exception
"Unhandled exception at 0x00007FFF269954D8 in Boost_Serialization.exe: Microsoft C++ exception: std::length_error at memory location 0x000000829D94F7B0."
Not sure what's wrong.
#include <iostream>
#include <vector>
#include <fstream>
#include <boost/serialization/vector.hpp>
#include<boost/archive/binary_oarchive.hpp>
#include<boost/archive/binary_iarchive.hpp>
#include<boost/serialization/binary_object.hpp>
class Info
{
private:
// Allow serialization to access non-public data members.
friend class boost::serialization::access;
//template<class Archive>
//void save(Archive & ar, const unsigned int version) const { ar & filenames;}
template<class Archive>
void load(Archive & ar, const unsigned int version) { ar & filenames; }
BOOST_SERIALIZATION_SPLIT_MEMBER()
std::vector<std::string> filenames;
public:
void AddFilename(const std::string& filename);
void Print() const;
};
void Info::Print() const { std::copy(filenames.begin(),filenames.end(),std::ostream_iterator<std::string>(std::cout, "\n")); }
void Info::AddFilename(const std::string& filename) { filenames.push_back(filename); }
int main(int argc, char** argv)
{
std::vector<Info> infs;
Info info1, info2;
info1.AddFilename("ThisFile.txt");
info2.AddFilename("ThatFile.txt");
info2.AddFilename("OtherFile.txt");
info2.AddFilename("ABC");
info2.AddFilename("123");
info2.AddFilename("XYZ");
infs.push_back(info1);
infs.push_back(info2);
// Save filename data contained in Info object
{
std::ofstream Obj_ofstream("data.dat", std::ios::binary);
boost::archive::binary_oarchive op_archive(Obj_ofstream);
op_archive << boost::serialization::make_binary_object(&infs, sizeof(infs));
//Obj_ofstream.close();
}
// Restore from saved data and print to verify contents
std::vector<Info> restored_info;
{
std::ifstream Obj_ifstream("data.dat", std::ios::binary);
boost::archive::binary_iarchive ip_archive(Obj_ifstream);
ip_archive >> restored_info;
//Obj_ifstream.close();
}
//std::vector<Info>::const_iterator it = restored_info.begin();
//for (; it != restored_info.end(); ++it)
//{
// Info info = *it;
// info.Print();
//}
std::cout << "Testing : " << std::endl;
return 0;
You use BOOST_SERIALIZATION_SPLIT_MEMBER() and do not implement save method. However, you do not need to do that. Remove splitting and implement general void serialize(Archive & ar, const unsigned int version) method (note to include new header for vector serialization). It will look like this and run like a charm:
#include <iostream>
#include <vector>
#include <fstream>
#include <boost/serialization/vector.hpp>
#include<boost/archive/binary_oarchive.hpp>
#include<boost/archive/binary_iarchive.hpp>
#include<boost/serialization/binary_object.hpp>
#include<boost/serialization/vector.hpp>
class Info
{
private:
// Allow serialization to access non-public data members.
friend class boost::serialization::access;
//template<class Archive>
//void save(Archive & ar, const unsigned int version) const { ar & filenames;}
template<class Archive>
void serialize(Archive & ar, const unsigned int version) { ar & filenames; }
std::vector<std::string> filenames;
public:
void AddFilename(const std::string& filename);
void Print() const;
};
void Info::Print() const { std::copy(filenames.begin(), filenames.end(), std::ostream_iterator<std::string>(std::cout, "\n")); }
void Info::AddFilename(const std::string& filename) { filenames.push_back(filename); }
int main(int argc, char** argv)
{
std::vector<Info> infs;
Info info1, info2;
info1.AddFilename("ThisFile.txt");
info2.AddFilename("ThatFile.txt");
info2.AddFilename("OtherFile.txt");
info2.AddFilename("ABC");
info2.AddFilename("123");
info2.AddFilename("XYZ");
infs.push_back(info1);
infs.push_back(info2);
// Save filename data contained in Info object
{
std::ofstream Obj_ofstream("data.dat", std::ios::binary);
boost::archive::binary_oarchive op_archive(Obj_ofstream);
op_archive << infs;
//Obj_ofstream.close();
}
// Restore from saved data and print to verify contents
std::vector<Info> restored_info;
{
std::ifstream Obj_ifstream("data.dat", std::ios::binary);
boost::archive::binary_iarchive ip_archive(Obj_ifstream);
ip_archive >> restored_info;
//Obj_ifstream.close();
}
std::vector<Info>::const_iterator it = restored_info.begin();
for (; it != restored_info.end(); ++it)
{
Info info = *it;
info.Print();
}
std::cout << "Testing : " << std::endl;
system("PAUSE");
return 0;
}

How to serialize object with shared_ptr member using boost

There are abstract I1 and derived C1.
There are abstract I2 and derived C2.
I1 have shared_ptr<I2>. How can I make them serializable using boost serializaton? I am attemtping do it, but my application get exception.
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
boost::archive::binary_oarchive oa(ss);
oa.register_type<I1>();
oa.register_type<C1>();
oa.register_type<I2>();
oa.register_type<C2>();
oa << c2;
boost::archive::binary_iarchive ia(ss);
//ia.register_type<I1>(); // cannot instantiate abstract class
ia.register_type<C1>();
//ia.register_type<I2>(); // cannot instantiate abstract class
ia.register_type<C2>();
ia >> c2;
}
The boost serialization documentation says here interesting things about BOOST_CLASS_EXPORT:
... BOOST_CLASS_EXPORT ...
Hence, the need for export is implied by the usage of a derived class that is manipulated via a pointer or reference to its base class.
Your p pointer does exactly that. Adding these macros to your code also gets rid of the ugly explicit register_type() calls from your main, which is also nice :)
So, this code seems to compile and work in VS2014:
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/export.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
BOOST_CLASS_EXPORT(I1)
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
BOOST_CLASS_EXPORT(C1)
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
BOOST_CLASS_EXPORT(I2)
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
BOOST_CLASS_EXPORT(C2)
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
boost::archive::binary_oarchive oa(ss);
oa << c2;
boost::archive::binary_iarchive ia(ss);
ia >> c2;
}
It is interesting, though, that the statement from Boost docs is obviously not true for all compilers, and lots of code examples on the internets just don't work in VS2014.
I think you aren't supposed to register your pure virtual classes on the output archive either.
Alternatively, you can use export. After defining your classes, replace the rest of your code with this:
#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(C1)
BOOST_CLASS_EXPORT(C2)
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
{
boost::archive::binary_oarchive oa(ss);
oa << c2;
}
boost::archive::binary_iarchive ia(ss);
ia >> c2;
}
I put the output archive into a separate block. I think it most likely works without it but I want to be sure that everything is flushed (via going out of scope).
Add
BOOST_SERIALIZATION_ASSUME_ABSTRACT(I1)
BOOST_SERIALIZATION_ASSUME_ABSTRACT(I2)
As per the documentation http://www.boost.org/doc/libs/1_39_0/libs/serialization/doc/traits.html#abstract
UPDATE
I've just verified with VS2013RTM and Boost 1_55, it JustWorks(TM), I've
removed the type registrations for abstract bases (they can never be concretely loaded from the archive anyways)
added
#pragma warning(disable: 4244)
#include <boost/config/warning_disable.hpp>
at the top of the file to silence known chatty warnings
Code compiled and runs without error. For good style, you should probably
serialize c2 through a pointer (stack variables can subtly derail object tracking)
Here's the full code I ended up with:
#pragma warning(disable: 4244)
#include <boost/config/warning_disable.hpp>
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
int main()
{
boost::shared_ptr<I2> c2(new C2);
std::string s;
std::stringstream ss(s);
boost::archive::text_oarchive oa(ss);
oa.register_type<C1>();
oa.register_type<C2>();
oa << c2;
std::cout << "Serialized form: '" << ss.str() << "'\n";
boost::archive::text_iarchive ia(ss);
ia.register_type<C1>();
ia.register_type<C2>();
ia >> c2;
}
And here's the output:

Boost Serialization Segfault

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)