提升结构和类的融合序列类型和名称识别

Posted

技术标签:

【中文标题】提升结构和类的融合序列类型和名称识别【英文标题】:Boost fusion sequence type and name identification for structs and class 【发布时间】:2014-10-15 10:36:23 【问题描述】:

我正在尝试将 boost fusion 用于我的一个项目,并且我正在研究如何获取结构和类的类型名称和变量名称。

#include <typeinfo>
#include <string>
#include <iostream>
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/adapt_adt.hpp>

#include <boost/lexical_cast.hpp>

using namespace boost::fusion;

struct Foo

    int integer_value;
    bool boolean_value;
;


class Bar

    int integer_value;
    bool boolean_value;
public:
    Bar(int i_val, bool b_val):integer_value(i_val),boolean_value(b_val) 
    int     get_integer_value() const        return integer_value; 
    void    set_integer_value(int i_val)     integer_value = i_val; 
    bool    get_boolean_value() const        return boolean_value; 
    void    set_boolean_value(bool b_val)    boolean_value = b_val; 
;

BOOST_FUSION_ADAPT_STRUCT(
    Foo,
    (int, integer_value)
    (bool, boolean_value)    
)

BOOST_FUSION_ADAPT_ADT(
    Bar,
    (int, int, obj.get_integer_value() , obj.set_integer_value(val))
    (bool, bool, obj.get_boolean_value(), obj.set_boolean_value(val))    
)


struct DisplayMembers


    template <typename T>
    void operator()(T& t) const 
        std::cout << typeid(t).name() << " : " << boost::lexical_cast<std::string>(t) << std::endl;
    

;

int main(int argc, char *argv[]) 

  struct Foo f =  33, false;
  for_each(f, DisplayMembers());

  Bar b(34,true);
  for_each(b, DisplayMembers());
  return 0;

在上面的例子中,结果是

int : 33
bool : 0
struct boost::fusion::extension::adt_attribute_proxy<class Bar,0,0> : 34
struct boost::fusion::extension::adt_attribute_proxy<class Bar,1,0> : 1

我想要的结果是

int : integer_value : 33
bool : boolean_value : 0
int : integer_value : 34
bool : boolean_value : 1

【问题讨论】:

typeid(t).name() 具有实现定义的行为。 DisplayMembers 函子需要接受T const&amp;,因为适应 ADT 的结构仅按值返回(并且临时对象不绑定到非常量引用,除非您使用 MSVC 编译器扩展) 【参考方案1】:

如果您使用的是 C++14,我将通过 sehe 提炼出更简单的答案

#include <iostream>

#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/mpl/range_c.hpp>

struct MyStruct 
    std::string foo;
    double bar;
;

BOOST_FUSION_ADAPT_STRUCT(MyStruct,
                          foo,
                          bar)

namespace fuz = boost::fusion;
namespace mpl = boost::mpl;

int main(int argc, char* argv[]) 
  MyStruct dummy"yo",3.14;

  fuz::for_each(mpl::range_c<
                unsigned, 0, fuz::result_of::size<MyStruct>::value>(),
                [&](auto index)
      std::cout << "Name: "
                << fuz::extension::struct_member_name<MyStruct,index>::call()
                << " Value: "
                << fuz::at_c<index>(dummy) << std::endl; 
    );


输出:

Name: foo Value: yo
Name: bar Value: 3.14

看live on coliru

【讨论】:

不错。我想说的是,它对于生产用途来说几乎是合理的【参考方案2】:

boost::fusion::extension::struct_member_name&lt;S, N::value&gt; 可以访问这些名称。

这是我使用的通用融合对象访问器:

namespace visitor 

    template <typename Flavour, typename T> struct VisitorApplication;

    namespace detail
    
        template <typename V, typename Enable = void>
        struct is_vector : boost::mpl::false_  ;

        template <typename T>
        struct is_vector<std::vector<T>, void> : boost::mpl::true_  ;

        namespace iteration
        
            // Iteration over a sequence
            template <typename FusionVisitorConcept, typename S, typename N>
                struct members_impl
                
                    // Type of the current member
                    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;

                    static inline void handle(FusionVisitorConcept& visitor, const S& s)
                    
                        visitor.start_member(name_t::call());
                        VisitorApplication<FusionVisitorConcept, current_t>::handle(visitor, boost::fusion::at<N>(s));
                        visitor.finish_member(name_t::call());
                        members_impl<FusionVisitorConcept, S, next_t>::handle(visitor, s);
                    
                ;

            // End condition of sequence iteration
            template <typename FusionVisitorConcept, typename S>
                struct members_impl<FusionVisitorConcept, S, typename boost::fusion::result_of::size<S>::type>
                
                    static inline void handle(FusionVisitorConcept const&, const S&)  /*Nothing to do*/ 
                ;

            // Iterate over struct/sequence. Base template
            template <typename FusionVisitorConcept, typename S>
                struct Struct : members_impl<FusionVisitorConcept, S, boost::mpl::int_<0>> ;

     // iteration

    template <typename FusionVisitorConcept, typename T>
        struct array_application
        
            typedef array_application<FusionVisitorConcept, T> type;

            typedef typename T::value_type value_type;

            static inline void handle(FusionVisitorConcept& visitor, const T& t)
            
                visitor.empty_array();
                for (auto& el : t)
                    VisitorApplication<FusionVisitorConcept, value_type>::handle(visitor, el);
            
        ;

    template <typename FusionVisitorConcept, typename T>
        struct struct_application
        
            typedef struct_application<FusionVisitorConcept, T> type;

            static inline void handle(FusionVisitorConcept& visitor, const T& t)
            
                visitor.empty_object();
                iteration::Struct<FusionVisitorConcept, T>::handle(visitor, t);
            
        ;

    template <typename FusionVisitorConcept, typename T, typename Enable = void>
        struct value_application
        
            typedef value_application<FusionVisitorConcept, T> type;

            static inline void handle(FusionVisitorConcept& visitor, const T& t) 
                visitor.value(t);
            
        ;

    template <typename FusionVisitorConcept, typename T>
        struct value_application<FusionVisitorConcept, boost::optional<T> >
        
            typedef value_application<FusionVisitorConcept, boost::optional<T> > type;

            static inline void handle(FusionVisitorConcept& visitor, const boost::optional<T>& t) 
                if (t)
                    VisitorApplication<FusionVisitorConcept, T>::handle(visitor, *t);
                else
                    ; // perhaps some default action?
            
        ;

    template <typename FusionVisitorConcept, typename T>
        struct select_application
        
            typedef
                //typename boost::mpl::eval_if<boost::is_array<T>,                  boost::mpl::identity<array_application<FusionVisitorConcept, T>>,
                typename boost::mpl::eval_if<detail::is_vector<T>,                  boost::mpl::identity<array_application <FusionVisitorConcept, T>>,
                typename boost::mpl::eval_if<boost::fusion::traits::is_sequence<T>, boost::mpl::identity<struct_application<FusionVisitorConcept, T>>,
                boost::mpl::identity<value_application<FusionVisitorConcept, T>>
                > >::type type;
        ;

     // detail

    template <typename FusionVisitorConcept, typename T>
        struct VisitorApplication : public detail::select_application<FusionVisitorConcept, T>::type
    
    ;


template <typename FusionVisitorConcept, typename T>
void apply_fusion_visitor(FusionVisitorConcept& visitor, T const& o)

    visitor::VisitorApplication<FusionVisitorConcept, T>::handle(visitor, o);

您可以通过提供访问者来使用它,例如对于类似 xml 的输出:

struct DisplayMemberVisitor 
    typedef std::string result_type;

    DisplayMemberVisitor()  ss << std::boolalpha; 

    std::string complete()  return ss.str(); 

    void start_member (const char* name)  
        ss << "<" << name << ">";
    
    void finish_member(const char* name)  
        ss << "</" << name << ">";
    

    template <typename T> void value(T const& value) 
        ss << value;
    

    void empty_object()  
    void empty_array()   

private:
    std::stringstream ss;
;

查看 Live On Coliru 它在哪里打印(包括一些调试输出):

&lt;integer_value&gt;33&lt;/integer_value&gt;&lt;boolean_value&gt;false&lt;/boolean_value&gt;&lt;integer_value&gt;34&lt;/integer_value&gt;&lt;boolean_value&gt;true&lt;/boolean_value&gt;

注意,ADT 适配宏不包含名称(因为没有可用的名称)。您可以很容易地创建一个宏 FUSION_ADAPT_KEYD_ADT,它也接受一个名称并生成 boost::fusion::extension::struct_member_name 的相关特化。

奖励材料

为适应 ADT 的成员添加成员名称特征

这是一种简单的方法,显示需要完成的工作量很小。

#define MY_ADT_MEMBER_NAME(CLASSNAME, IDX, MEMBERNAME)                                                                                   \
        namespace boost  namespace fusion  namespace extension                                                                        \
            template <> struct struct_member_name<CLASSNAME, IDX>  typedef char const *type; static type call()  return #MEMBERNAME;  \
        ;   

MY_ADT_MEMBER_NAME(Bar, 0, integer_value)
MY_ADT_MEMBER_NAME(Bar, 1, boolean_value)

这定义了一个宏来避免大部分重复。如果您是 BOOST_PP 高手,您可以以某种方式将其编入 adt_ex.hpp¹ 的各种标题中,因此您可以改为:

BOOST_FUSION_ADAPT_ADT(Bar, // NOTE THIS PSEUDO-CODE
    (integer_value, int,  int,  obj.get_integer_value(), obj.set_integer_value(val))
    (boolean_value, bool, bool, obj.get_boolean_value(), obj.set_boolean_value(val)))

现在这里是适应 ADT 的技巧 Live On Coliru

¹如果您有兴趣,这里有一个准备好的 adt_ex 树的 tarball(放入 about adt.hpp):adt_ex.tgz 作为起点。它只是 adt*,但将宏和标头保护重命名为 adt_ex*

【讨论】:

我遇到了一个类似的访问者,它适用于适应 boost fusion 的结构序列。当我对一个类进行排序时,似乎不支持 name_t::call() 方法。所以我很好奇如何获取类成员变量信息。 @balasbellobas 这是 Fusion 的 *ADAPT_ADT 的限制。有关如何解决此问题的想法,请参阅我的最后一段。同时,查看演示 Live On Coliru 感谢您的回复。我有一种类似的方法来递归地了解结构定义。我不明白如何克服 call() 方法的限制。融合适配宏定义为 BOOST_FUSION_ADAPT_ADT( type_name, ... )。如果我的理解是正确的 type_name 对应于类名。 @balasbellobas 呵呵呵呵。你两次说“我有类似的方法”。最好 (a) 分享 (b) 提及您知道只是 *ADAPT_ADT 造成了麻烦。我会看看我能想出什么 啊!我现在明白了。您认为 *ADAPT_STRUCT 用于“结构”,因此 *ADAPT_ADT 必须用于“类”。我没有检测到任何这种逻辑,因为在 C++ 中classstruct 完全相同。所以你的意思是说使用*ADT抽象一个类。

以上是关于提升结构和类的融合序列类型和名称识别的主要内容,如果未能解决你的问题,请参考以下文章

Java基础RTTI与反射之Java

RTTI与反射之Java

设计模式分类

java 类型信息

RTTI和反射小结

Swift 结构体和类的区别