如何为boost融合贴图添加boost归档序列化支持?

时间:2014-04-15 19:54:05

标签: c++ serialization boost

我希望通过boost序列化界面添加能够序列化boost融合图的功能。我尝试过以下方法:

#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;
}

这给了我编译错误:

struct boost::fusion::map<boost::fusion::pair<fieldOne, int>, 
boost::fusion::pair<fieldTwo, double> >’ has no member named ‘serialize’
显然,我需要自己实现这一点。在网上搜索了一些指导之后,我找到了这些答案

Boost fusion serialization of a class using BOOST_FUSION_ADAPT_ADT

How to serialize fusion::vector?

然而,所有这些答案(以及网络上的许多其他答案)都依赖于使用自定义函数调用序列化,例如:

fusion_serialize(ar, m);

但是,我想以我可以调用的方式序列化地图:

ar & m;

这样我就可以在其他模板函数中使用序列化了。反正有没有实现这个目标?

我已经尝试将此添加到我的源文件

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);
    }
  }
}

但是,这太普遍了,因为模板将匹配任何类型并且如果它不是融合映射则生成编译错误。我不知道如何修改上述内容,以便serialize函数定义仅适用于boost::fusion::map类型。有什么建议吗?

2 个答案:

答案 0 :(得分:4)

您可以为任何Fusion地图一般实施序列化:

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));
        }

} }

现在,这有效:

#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;
}

查看 Live On Coliru

打印:

22 serialization::archive 10 0 0 42 3.1415926535897931

反序列化同时实施。


为了完整性:

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

答案 1 :(得分:1)

(这是一个C ++ 11解决方案,也许它可以激发一个带有Boost.PP的C ++ 98,但它将会有更多的工作。)

是,

  

[that]过于笼统,因为模板将匹配任何类型并生成   如果它不是融合图,则编译错误。

...这使得serialize函数与序列化库本身中包含的其他声明发生冲突(ambiguate)。

因此,在@sehe答案之上构建更多通用性,您可以使用以下内容。请注意,在这种情况下,您不需要#include或假设序列,因此它适用于所有融合序​​列。关键是使用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);
}

}}

示例:

#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);
}

输出:

<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>