Page 1 of 1

Polymorphic serialization with boost

Posted: Tue Nov 26, 2019 2:00 pm
by sschmitt
Dear all!

I have a problem using RCF 2.2.0.0 with boost serialization for polymorphic types.

I want to change an instance of B passed via a reference of base class A. Using SF it works as expected:

Code: Select all

Calling the I_SetService Set() method.
B::SF_serialize
A::SF_serialize
B::SF_serialize
A::SF_serialize
B::set() 42
B::SF_serialize
A::SF_serialize
B::SF_serialize
A::SF_serialize
42 expected 42
However, with boost, the derived class' serialization is not called and the value is unchanged.

Code: Select all

Calling the I_SetService Set() method.
A::boost_serialize saving
B::boost_serialize saving
A::boost_serialize loading
B::boost_serialize loading
B::set() 42
A::boost_serialize saving
A::boost_serialize loading
1 expected 42
I'm aware that both RCF 2.x and boost serialization are deprecated. However, for the project I'm working on it's a requirement to use at least the boost serialization.

Could you please give me a hint on how to make it work?

You can find my minimal example below.

Thanks in advance,

Sebastian

Code: Select all

#include <fstream>
#include <iostream>

#include "RCF/RCF.hpp"

#ifndef RCF_USE_BOOST_SERIALIZATION
#include "SF/Registry.hpp"
#include "SF/SerializeParent.hpp"
#endif

#include <boost/serialization/export.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/serialization.hpp>

class A
{
public:
	virtual void set(int /*i*/)
	{
		throw std::runtime_error("A::set() is not expected to be called");
	};

#ifndef RCF_USE_BOOST_SERIALIZATION
	void serialize(SF::Archive& /*ar*/) { std::cout << "A::SF_serialize\n"; }
#endif

private:
	friend class boost::serialization::access;
	template <typename Archive>
	void serialize(Archive& ar, unsigned int const /* version */)
	{
		std::cout << "A::boost_serialize "
		          << (typename Archive::is_loading() ? "loading" : "saving") << '\n';
	}
};

BOOST_CLASS_EXPORT(A)

class B : public A
{
public:
	B() : s(1){};
	void set(int i) override
	{
		std::cout << "B::set() " << i << "\n";
		s = i;
	}
	int s;

#ifndef RCF_USE_BOOST_SERIALIZATION
	void serialize(SF::Archive& ar)
	{
		std::cout << "B::SF_serialize\n";
		SF::serializeParent<A>(ar, *this);
		ar& s;
	}
#endif

private:
	friend class boost::serialization::access;
	template <typename Archive>
	void serialize(Archive& ar, unsigned int const /* version */)
	{
		using namespace boost::serialization;
		ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(A);
		ar& make_nvp("s", s);
		std::cout << "B::boost_serialize "
		          << (typename Archive::is_loading() ? "loading" : "saving") << '\n';
	}
};

BOOST_CLASS_EXPORT(B)

RCF_BEGIN(I_SetService, "I_SetService")
RCF_METHOD_V2(void, Set, A&, int)
RCF_END(I_SetService)

class SetService
{
public:
	void Set(A& a, int i) { a.set(i); }
};

int main()
{
	try {

#ifndef RCF_USE_BOOST_SERIALIZATION
		SF::registerType<B>("B");
		SF::registerBaseAndDerived<A, B>();
#endif

		RCF::RcfInitDeinit rcfInit;
		RCF::RcfServer server(RCF::TcpEndpoint("127.0.0.1", 50001));

		SetService printService;
		server.bind<I_SetService>(printService);
		server.start();

		RcfClient<I_SetService> client(RCF::TcpEndpoint("127.0.0.1", 50001));

		B b = B();
		A& a = b;
		int const value = 42;
		std::cout << "Calling the I_SetService Set() method." << std::endl;

		client.Set(a, value);

		std::cout << b.s << " expected " << value << '\n';

	} catch (const RCF::Exception& e) {
		std::cout << "Error: " << e.what() << std::endl;
	}

	return 0;
}

Re: Polymorphic serialization with boost

Posted: Tue Nov 26, 2019 10:58 pm
by jarl
Hi there,

Polymorphic serialization with Boost is a tricky area, and my recommendation would be to use SF instead, as I think it's much more straightforward.

If you have to use Boost, I think you'll need to register the derived class so that Boost.Serialization knows about it ahead of time. In some of our sample code I spotted the BOOST_CLASS_EXPORT() macro:

https://www.boost.org/doc/libs/1_71_0/l ... tml#export

, which may be what you need.

Also, see if you can write a small program that only serializes and deserializes the A and B classes, and that doesn't involve RCF at all. If you get that working, then I think it should work in RCF as well.