Ælex
Ælex

Reputation: 14829

public empty constructors needed by serialization

I am chasing a rabbit down the hole, by trying to figure out the following:

  1. I do not want empty public constructors (they can, and will create issues)
  2. Two template classes must remain ignorant of each-other, else I will have template parameter cyclic problems
  3. A third class apparently needs access to both previous classes, as it is the one that is being (de)serialized

A minified example is shown below:

template <class s_trait>
class state
{
public:
    state(s_trait);
private:
    state() = default;
    friend class boost::serialization::access;
    template <typename archive>
    void serialize(archive &, const unsigned int);
};

template <class a_trait>
class action
{
public:
    action(a_trait);
private:
    action() = default;
    friend class boost::serialization::access;
    template <typename archive>
    void serialize(archive &, const unsigned int);
};

template <class state_type, class action_type>
class policy
{
public:
     /// ... some public methods
private:
    friend class boost::serialization::access;
    std::unordered_map<state_class,
                       std::unordered_map<action_class,
                                          double>> __policies__;
    template <typename archive>
    void serialize(archive &, const unsigned int);
};

The above skeleton (without the boring details) has the following design issues:

However, compiling an app using those classes creates issues arising from std::pair, which seem to stem from boost::serialization::access, which in turn appear to be from the fact that class policy needs access to the default empty constructors when calling serialize:

/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘constexpr std::pair<_T1, _T2>::pair() [with _T1 = const relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >; _T2 = std::unordered_map<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double, relearn::hasher<relearn::action<mu
mbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::allocator<std::pair<const relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double> > >]’:
/usr/include/boost/serialization/access.hpp:132:9:   required from ‘static void boost::serialization::access::construct(T*) [with T = std::pair<const relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, std::unordered_map<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double, rel
earn::hasher<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::allocator<std::pair<const relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double> > > >]’
/usr/include/boost/serialization/serialization.hpp:93:22:   required from ‘void boost::serialization::load_construct_data(Archive&, T*, unsigned int) [with Archive = boost::archive::binary_iarchive; T = std::pair<const relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, std::unordered_map<relearn::act
ion<mumbler::action<std::__cxx11::basic_string<char> > >, double, relearn::hasher<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::allocator<std::pair<const relearn::action<mumbler::action<std::__cxx11::b
asic_string<char> > >, double> > > >]’
/usr/include/boost/serialization/serialization.hpp:158:28:   required from ‘void boost::serialization::load_construct_data_adl(Archive&, T*, unsigned int) [with Archive = boost::archive::binary_iarchive; T = std::pair<const relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, std::unordered_map<relearn
::action<mumbler::action<std::__cxx11::basic_string<char> > >, double, relearn::hasher<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::allocator<std::pair<const relearn::action<mumbler::action<std::__cxx
11::basic_string<char> > >, double> > > >]’
/usr/include/boost/serialization/detail/stack_constructor.hpp:54:54:   required from ‘boost::serialization::detail::stack_construct<Archive, T>::stack_construct(Archive&, unsigned int) [with Archive = boost::archive::binary_iarchive; T = std::pair<const relearn::state<mumbler::state<std::__cxx11::basic_string<char> >
 >, std::unordered_map<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double, relearn::hasher<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::allocator<std::pair<const relearn::act
ion<mumbler::action<std::__cxx11::basic_string<char> > >, double> > > >]’
/usr/include/boost/serialization/unordered_map.hpp:45:55:   required from ‘void boost::serialization::stl::archive_input_unordered_map<Archive, Container>::operator()(Archive&, Container&, unsigned int) [with Archive = boost::archive::binary_iarchive; Container = std::unordered_map<relearn::state<mumbler::state<std::
__cxx11::basic_string<char> > >, std::unordered_map<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double, relearn::hasher<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::allocator
<std::pair<const relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double> > >, relearn::hasher<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > > >, std::allocator<std::pair<const relearn::state<mu
mbler::state<std::__cxx11::basic_string<char> > >, std::unordered_map<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double, relearn::hasher<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >, std::equal_to<relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >
 >, std::allocator<std::pair<const relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >, double> > > > > >]’
/usr/include/boost/serialization/unordered_collections_load_imp.hpp:66:14:   [ skipping 24 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/archive/detail/iserializer.hpp:618:18:   required from ‘void boost::archive::load(Archive&, T&) [with Archive = boost::archive::binary_iarchive; T = relearn::policy<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, relearn::action<mumbler::action<std::__cxx11::basic_string<char> >
 > >]’
/usr/include/boost/archive/detail/common_iarchive.hpp:66:22:   required from ‘void boost::archive::detail::common_iarchive<Archive>::load_override(T&, int) [with T = relearn::policy<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, relearn::action<mumbler::action<std::__cxx11::basic_string<char> > >
 >; Archive = boost::archive::binary_iarchive]’
/usr/include/boost/archive/basic_binary_iarchive.hpp:76:7:   required from ‘void boost::archive::basic_binary_iarchive<Archive>::load_override(T&, int) [with T = relearn::policy<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, relearn::action<mumbler::action<std::__cxx11::basic_string<char> > > >; 
Archive = boost::archive::binary_iarchive]’
/usr/include/boost/archive/binary_iarchive_impl.hpp:62:9:   required from ‘void boost::archive::binary_iarchive_impl<Archive, Elem, Tr>::load_override(T&, int) [with T = relearn::policy<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, relearn::action<mumbler::action<std::__cxx11::basic_string<char>
 > > >; Archive = boost::archive::binary_iarchive; Elem = char; Tr = std::char_traits<char>]’
/usr/include/boost/archive/detail/interface_iarchive.hpp:60:9:   required from ‘Archive& boost::archive::detail::interface_iarchive<Archive>::operator>>(T&) [with T = relearn::policy<relearn::state<mumbler::state<std::__cxx11::basic_string<char> > >, relearn::action<mumbler::action<std::__cxx11::basic_string<char> > 
> >; Archive = boost::archive::binary_iarchive]’
/home/alex/codez/mumbler/src/agent.cpp:9:11:   required from here
/home/alex/codez/mumbler/src/relearn/src/relearn.hpp:90:5: error: ‘relearn::state<state_trait, value_type>::state() [with state_trait = mumbler::state<std::__cxx11::basic_string<char> >; value_type = double]’ is private
     state() = default;

As far as I can see, I have two options:

  1. leave a public empty default constructor
  2. forward-declare class policy, then befriend it - however this creates the issue that the classes befriending it need be aware of its template parameters, thus they need to be aware of each-other (state-action) which is what I would like to avoid.

Is there some other way to go around this without exposing public empty constructors?

edit

This is not a duplicate, I know how to (de)serialize, the question is about hiding the default constructor for only the (de)serializer to have access to it.

Upvotes: 1

Views: 153

Answers (1)

John Zwinck
John Zwinck

Reputation: 249093

Make your default constructors protected, then make a derived class from each that has a public constructor but hide those derived classes from the outside entirely (put them in a detail header file that nobody else includes). Then your policy class can use the derived classes internally, for serialization and storage, but only expose them as (sliced) base class objects.

Upvotes: 2

Related Questions