Monday, 31 August 2009

More on using Boost Serialization and errors you may encounter

I sometimes need to store different types of objects in a container.

In main.cpp:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <fstream>
#include <iostream>

using namespace
boost;
using namespace
std;

class
Actor
{

public
:
Actor( void ) {}
Actor( char gender )
:
gender( gender )
{}

virtual
~Actor() {}
char
Gender( void ) const { return gender; }
private
:
friend class
boost::serialization::access;
template
<class Archive>
void
serialize(Archive & ar, const unsigned int version)
{

ar & gender;
}

char
gender;
};


class
Crit: public Actor
{

public
:
Crit( void ) {}
Crit( int age )
:
age( age )
{
}

int
Age( void ) const { return age; }
private
:
friend class
boost::serialization::access;
template
<class Archive>
void
serialize(Archive & ar, const unsigned int version)
{

ar & boost::serialization::base_object< Actor >( *this );
ar & age;
}

int
age;
};


BOOST_CLASS_EXPORT_GUID( Crit, "Crit")

class
Food: public Actor
{

public
:
Food( void ) {}
Food( int energy )
:
energy( energy )
{
}

int
Energy( void ) const { return energy; }
private
:
friend class
boost::serialization::access;
template
<class Archive>
void
serialize(Archive & ar, const unsigned int version)
{

ar & boost::serialization::base_object< Actor >( *this );
ar & energy;
}

int
energy;
};


BOOST_CLASS_EXPORT_GUID( Food, "Food")

template
< typename T >
void
save( T& data, string filename )
{

ofstream ofs( filename.c_str() );
boost::archive::text_oarchive oa( ofs );
oa & data;
}


template
< typename T >
void
load( T& data, string filename )
{

ifstream ofs( filename.c_str() );
boost::archive::text_iarchive ia( ofs );
ia & data;
}


void
TestAlpha( void )
{

shared_ptr< Crit > critAlpha( new Crit( 18 ) );
shared_ptr< Crit > critBravo( new Crit( 19 ) );
shared_ptr< Food > foodAlpha( new Food( 20 ) );
shared_ptr< Food > foodBravo( new Food( 21 ) );
vector< shared_ptr< Actor > > conAlpha;
vector< shared_ptr< Actor > > conBravo;

conAlpha.push_back( critAlpha );
conAlpha.push_back( foodAlpha );

// save
string filename = "sav.sav";
save( conAlpha, filename );

// test
assert( critBravo->Age() == 19 );
assert( foodBravo->Energy() == 21 );

// load
load( conBravo, filename );

// test
critBravo = dynamic_pointer_cast< Crit >( conBravo[ 0 ] );
assert( critBravo->Age() == 18 );
foodBravo = dynamic_pointer_cast< Food >( conBravo[ 1 ] );
assert( foodBravo->Energy() == 20 );
}


int
main( int argc, const char* argv[] )
{

TestAlpha();
return
EXIT_SUCCESS;
}

In Makefile (remember to put a real tab before where it says 'g++'):
all:
g++ -g -Wall testBoostSerializationInheritance.cpp -lboost_serialization


Error you may encounter 1:
/usr/include/boost/shared_ptr.hpp:209: error: cannot dynamic_cast ‘r->boost::shared_ptr::px’ (of type ‘class Actor* const’) to type ‘class Food*’ (source type is not polymorphic)
To fix this error, make sure that you have a virtual function in your base class. For example:
virtual ~Actor() {}

Error you may encounter 2:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): unregistered class

To fix this error, make sure that you register your classes by doing for example the following:
BOOST_CLASS_EXPORT_GUID( Crit, "Crit")

Error you may encounter 3:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): unregistered void cast

To fix this error, make sure that you put the following line in your derived class serialization method:
ar & boost::serialization::base_object<>( *this );

7 comments:

  1. Hi Paul,

    While doing same, i am getting below error message
    cilpp0_cfg.cpp:36: error: expected initializer before ‘<’ token
    cilpp0_cfg.cpp:36: error: ISO C++ forbids declaration of ‘type name’ with no type
    cilpp0_cfg.cpp:36: error: wrong number of template arguments (1, should be 2)
    /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_pair.h:68: error: provided for ‘template class _T1, class _T2 struct std::pair’

    can u suggest, what could be the cause of error ?

    ReplyDelete
  2. Hi cpgarg10,

    I can't think of anything that might be the cause of the error. Can you show me some code around line 36?

    ReplyDelete
  3. Hi cpgarg10,

    I reread your error message and noticed that your code requires two template arguments and at the moment your code only has one. You may need to give a bit more information. For example,

    function< float, int >()

    or

    dataStructure< float, float > myDataStructure

    I hope that helps.

    ReplyDelete
  4. Hi Binh Nugyen,
    I am sorry for not following your replies. Actually problem is, you can't use macro BOOST_CLASS_EXPORT_GUID from any name space
    For example:
    namespace sample
    {
    BOOST_CLASS_EXPORT_GUID (XXX, "XXX");
    }

    My code for XML serialization was inside some namespace, so it was causing compilation error.

    To fix this problem, first i have forward declared my class and then exported these macro from global name space.
    Thanks for the wonderful article, it helped me a lot !!

    Regards,
    --cpgarg10

    ReplyDelete
  5. Hi cpgarg10!

    I'm glad you found this post useful. Thanks for taking the time to write about what you found out. :)

    ReplyDelete
  6. Your post is already about 4 years old and yet helps me over and over solving my boost::serialization problems. Thank you!

    ReplyDelete
  7. Hi Markus,

    That's awesome! Thanks for leaving a comment, much appreciated.

    ReplyDelete