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