C++使用boost fusion adapt_struct迭代到嵌套的struct字段中

min*_*hua 9 c++ reflection introspection boost-fusion template-meta-programming

两个stackoverflow 答案建议使用fusion adapt_struct迭代结构字段的方法.这种方法看起来不错.但是,如何迭代到一个本身就是结构的字段?

根据之前的答案,我想出了下面的代码.问题出在代码无法编译的"#if 0"子句中.作为替代解决方案,我创建了"decode()"函数来获取指向目标参数的void指针.这有效,但在编译时丢失了类型信息.有更好的解决方案吗?

struct Foo_s { int i; };
BOOST_FUSION_ADAPT_STRUCT( Foo_s,  (int, i) )

struct Bar_s { int v; Foo_s w; };
BOOST_FUSION_ADAPT_STRUCT( Bar_s, (int, v)  (Foo_s, w) )

struct AppendToTextBox {
    template <typename T> void operator()(T& t) const {
        int status = 0;
        const char *realname = abi::__cxa_demangle(typeid(t).name(), 0, 0, &status);
        printf("  typename: %s  value: %s  realname: %s\n", typeid(t).name(),
               boost::lexical_cast<std::string>(t).c_str(), realname);
        std::string rn(realname);
        if ( rn.rfind("_s") == rn.size()-2 ) {
#if 0 /* this can not compile */
            for_each(t, AppendToTextBox());
#else
            decode(&t, rn);
#endif
        }
    }
};

void decode(void *f, std::string & intype ) {
    if ( intype.find("Foo_s") == 0 ) 
        for_each( *(Foo_s *)f, AppendToTextBox());
};

int main(int argc, char *argv[]) {
  Bar_s f = { 2, { 3 } };
  for_each(f, AppendToTextBox());
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我在维基百科上看过而不是传递类型字符串"intype",你可以使用typeid和dynamic_cast.但这只会是一个小小的改进.我正在寻找一种更加固有的C++解决方案或者提升语言设计.

小智 11

一个例子,你可以在我的博客网站上看到你想要的东西.在这种情况下,它是一个使用嵌套结构的JSON序列化程序.自从我在Boost.Serialization库中看到它以来,它使用了"更多Boost"解决方案.(另见下文并住在Coliru.)

该解决方案使用结构的融合序列自适应和使用Boost.TypeTraits和特定类型的不同特征来递送对象成员的元函数(递归地).

您可以在googlecode corbasim项目的站点上看到相同解决方案的更复杂示例,以创建运行时自反API.

通用JSON序列化程序的代码清单:

在科利鲁看到它

#ifndef JSON_SERIALIZER_HPP
#define JSON_SERIALIZER_HPP

#include <boost/type_traits.hpp> // is_array, is_class, remove_bounds

#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/next_prior.hpp>

#include <boost/fusion/mpl.hpp>
#include <boost/fusion/adapted.hpp> // BOOST_FUSION_ADAPT_STRUCT

// boost::fusion::result_of::value_at
#include <boost/fusion/sequence/intrinsic/value_at.hpp>
#include <boost/fusion/include/value_at.hpp>

// boost::fusion::result_of::size
#include <boost/fusion/sequence/intrinsic/size.hpp>
#include <boost/fusion/include/size.hpp>

// boost::fusion::at
#include <boost/fusion/sequence/intrinsic/at.hpp>
#include <boost/fusion/include/at.hpp>

namespace json
{

// Forward
template < typename T >
struct serializer;

namespace detail
{

namespace iterator
{

template < typename S, typename N >
struct Comma
{
    template < typename Ostream >
    static inline void comma(Ostream& os)
    {
        os << ", ";
    }
};

template < typename S >
struct Comma< S, typename boost::mpl::prior< typename boost::fusion::result_of::size< S >::type >::type >
{
    template < typename Ostream >
    static inline void comma(Ostream& os)
    {
    }
};

// Iteracion sobre una estructura
template < typename S, typename N >
struct StructImpl
{
    // Tipo del campo actual
    typedef typename boost::fusion::result_of::value_at< S, N >::type current_t;
    typedef typename boost::mpl::next< N >::type next_t;
    typedef boost::fusion::extension::struct_member_name< S, N::value > name_t;

    template < typename Ostream >
    static inline void serialize(Ostream& os, const S& s)
    {
        os << "\"" << name_t::call() << "\": ";
        ::json::serializer< current_t >::serialize(os, boost::fusion::at< N >(s));

        // Insert comma or not    
        Comma< S, N >::comma(os);

        StructImpl< S, next_t >::serialize(os, s);
    }
};

// Fin de la iteracion sobre estructuras.
template < typename S >
struct StructImpl< S, typename boost::fusion::result_of::size< S >::type >
{
    template < typename Ostream >
    static inline void serialize(Ostream& os, const S& s)
    {
        // Nada que hacer
    }
};

// Iterador sobre una estructura. Template fachada.
template < typename S >
struct Struct : StructImpl< S, boost::mpl::int_< 0 > > {};

} // iterator

template < typename T >
struct array_serializer 
{
    typedef array_serializer< T > type;

    typedef typename boost::remove_bounds< T >::type slice_t;

    static const size_t size = sizeof(T) / sizeof(slice_t);

    template < typename Ostream >
    static inline void serialize(Ostream& os, const T& t)
    {
        os << "[";
        for(size_t idx=0; idx<size; idx++)
        {
            ::json::serializer< slice_t >::serialize(os, t[idx]);
            if (idx != size-1)
                os << ", ";
        }
        os << "]";
    }

};

template < typename T >
struct struct_serializer 
{
    typedef struct_serializer< T > type;

    template < typename Ostream >
    static inline void serialize(Ostream& os, const T& t)
    {
        os << "{";
        iterator::Struct< T >::serialize(os, t);
        os << "}";
    }
};

template < typename T >
struct arithmetic_serializer 
{
    typedef arithmetic_serializer< T > type;

    template < typename Ostream >
    static inline void serialize(Ostream& os, const T& t)
    {
        os << t;
    }
};

template < typename T >
struct calculate_serializer
{
    typedef
        typename boost::mpl::eval_if< boost::is_array< T >,
            boost::mpl::identity< array_serializer < T > >,
        //else
        typename boost::mpl::eval_if< boost::is_class< T >,
            boost::mpl::identity< struct_serializer < T > >,
        //else
            boost::mpl::identity< arithmetic_serializer < T > >
        >
        >::type type;

};

} // detail

template < typename T >
struct serializer : public detail::calculate_serializer < T >::type
{
};


} // json

#endif // JSON_SERIALIZER_HPP

//#include "json.hpp"
#include <iostream>

struct my_other_struct
{
    int my_other_integer;
};

struct my_struct
{
    int my_integer;

    typedef int my_array_t[2];
    my_array_t my_array;

    typedef my_other_struct my_other_structs_t[3];
    my_other_structs_t my_other_structs;
};

BOOST_FUSION_ADAPT_STRUCT(my_struct, (int, my_integer) (my_struct::my_array_t, my_array) (my_struct::my_other_structs_t, my_other_structs))
BOOST_FUSION_ADAPT_STRUCT(my_other_struct, (int, my_other_integer))


int main(int argc, char *argv[])
{
    my_struct s1 = my_struct { 1, { 42, -42 }, { { 11 }, { 22 }, { 33 } } };

    json::serializer< my_struct >::serialize(std::cout, s1);

    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • @minghua你可以编辑答案,以便与答案作者分享声誉 (2认同)