How do I add boost archive serialization support for boost fusion maps?

元气小坏坏 提交于 2020-01-02 06:57:11

问题


I'd like to add the functionality to be able to serialize boost fusion maps via the boost serialization interface. I've tried the following:

#include <iostream>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/include/map.hpp>
#include <boost/fusion/container/map/map_fwd.hpp>
#include <boost/fusion/include/map_fwd.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/fusion/include/at_key.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/archive/text_iarchive.hpp>

struct fieldOne {};
struct fieldTwo {};
typedef boost::fusion::map<
    boost::fusion::pair<fieldOne, int>,
    boost::fusion::pair<fieldTwo, double> 
  > tFusionMap;

int main() {
  tFusionMap map;
  boost::archive::text_iarchive ar(std::cin);

  std::cin >> map; /* no compile error */
  ar & map; /* compiler error: */

  return 0;
}

which gives me the compiler error:

struct boost::fusion::map<boost::fusion::pair<fieldOne, int>, 
boost::fusion::pair<fieldTwo, double> >’ has no member named ‘serialize’

so apparently I need to implement this myself. After searching the web for some guidance, I found these answers

Boost fusion serialization of a class using BOOST_FUSION_ADAPT_ADT

How to serialize fusion::vector?

however, all of theses answers (and many others on the web) rely on invoking the serialization with a custom function, for example:

fusion_serialize(ar, m);

However, I would like to serialize the map in a way that I can call:

ar & m;

so that I can use the serialization in other template functions. Is there anyway to achieve this?

I've tried adding this to my source file

namespace boost {
  namespace serialization {

    template<class Archive, typename T, std::size_t num_dims>
    void serialize( Archive & ar, T & map, const unsigned int version ) {
      fusion_serialize(ar,map);
    }
  }
}

However, this is too general as the template will match ANY type and generate compile errors if it is not a fusion map. I don't see how I can modify the above so that the serialize function definition only applies to boost::fusion::map types. Any suggestions?


回答1:


You can generically implement serialization for any Fusion map:

namespace boost { namespace serialization {

    struct saver {
        template <typename Ar, typename Pair>
            void operator()(Ar& ar, Pair& data) const
            {
                ar & data.second;
            }
    };

    template <typename Ar, typename... TArgs>
        void serialize(Ar& ar, boost::fusion::map<TArgs...>& fmap, unsigned /*version*/)
        {
            using phoenix::ref;
            using phoenix::arg_names::arg1;
            static const phoenix::function<saver> save {};

            fusion::for_each(fmap, save(ref(ar), arg1));
        }

} }

Now, this works:

#include <boost/archive/text_oarchive.hpp>
#include <iostream>

typedef boost::fusion::map<
    boost::fusion::pair<struct fieldOne, int>,
    boost::fusion::pair<struct fieldTwo, double> 
  > tFusionMap;

int main() {
    tFusionMap map { 42 , M_PI };
    boost::archive::text_oarchive oa(std::cout);
    oa & map;
}

See it Live On Coliru

Prints:

22 serialization::archive 10 0 0 42 3.1415926535897931

Deserialization is implemented at the same time.


For completeness:

#include <boost/fusion/include/map.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/phoenix/fusion.hpp>



回答2:


(This is a C++11 solution, maybe it can inspire a C++98 with Boost.PP but it is going to be so much more work.)

Yes,

[that] is too general as the template will match ANY type and generate compile errors if it is not a fusion map.

...which makes the serialize function conflict (ambiguate) with other declarations contained in the serialization library itself.

So, building some more genericity on top of @sehe answer, you can use the following. Note that you don't need to #include or assume a sequence in this case, so it works for all fusion sequences. The key is the use of template <class...> class Sequence.

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/support/is_sequence.hpp>
#include <boost/serialization/nvp.hpp>
#include <typeinfo> // typeid
#include <string>
#include <boost/fusion/support/pair.hpp>

namespace boost{
namespace fusion{

template<typename Archive>
struct item_serializer{
    Archive& ar;
    item_serializer(Archive& ar) : ar(ar){}
    template<typename T>
    void operator()(T& item) const{
        ar & boost::serialization::make_nvp((std::string("item_") + typeid(T).name()).c_str(), item);
    //  ar & BOOST_SERIALIZATION_NVP(item); // for more conservative xml tag name
    }
};

template<class Archive, class T1, class T2>
void serialize(Archive& ar, pair<T1, T2>& p, const unsigned int /*file_version*/){
    T2& second = p.second;
    ar & BOOST_SERIALIZATION_NVP(second);
}

template<
    class Archive, template <class...> class Sequence, typename... Args
    , typename = typename boost::enable_if_c<traits::is_sequence<Sequence<Args...>>::value>::type
>
void serialize(Archive& ar, Sequence<Args...>& s, const unsigned int /*file_version*/){
    item_serializer<Archive> sr(ar);
    for_each(s, sr);
}

}}

Example:

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/comparison/equal_to.hpp>
#include <sstream>

int main(){
    boost::fusion::vector<int, char, double> vector_src(
        3, '4', 5.41
    ), vector_dst;

    boost::fusion::map<
        boost::fusion::pair<struct fieldOne, int>,
        boost::fusion::pair<struct fieldTwo, double> 
    > map_src{42, M_PI}, map_dst;

    std::ostringstream oss;
    boost::archive::xml_oarchive oa(oss);
    oa << BOOST_SERIALIZATION_NVP(vector_src);
    oa << BOOST_SERIALIZATION_NVP(map_src);

    std::cout << oss.str();

    std::istringstream iss(oss.str());
    boost::archive::xml_iarchive ia(iss);
    ia >> BOOST_SERIALIZATION_NVP(vector_dst);
    ia >> BOOST_SERIALIZATION_NVP(map_dst);

    assert(vector_src == vector_dst);
    assert(map_src == map_dst);
}

Output:

<vector_src class_id="0" tracking_level="0" version="0">
    <item_i>3</item_i>
    <item_c>52</item_c>
    <item_d>5.4100000000000001</item_d>
</vector_src>
<map_src class_id="1" tracking_level="0" version="0">
    <item_N5boost6fusion4pairIZ4mainE8fieldOneiEE class_id="2" tracking_level="0" version="0">
        <second>42</second>
    </item_N5boost6fusion4pairIZ4mainE8fieldOneiEE>
    <item_N5boost6fusion4pairIZ4mainE8fieldTwodEE class_id="3" tracking_level="0" version="0">
        <second>3.1415926535897931</second>
    </item_N5boost6fusion4pairIZ4mainE8fieldTwodEE>
</map_src>


来源:https://stackoverflow.com/questions/23093198/how-do-i-add-boost-archive-serialization-support-for-boost-fusion-maps

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!