提升结构和类的融合序列类型和名称识别
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&
,因为适应 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<S, N::value>
可以访问这些名称。
这是我使用的通用融合对象访问器:
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 它在哪里打印(包括一些调试输出):
<integer_value>33</integer_value><boolean_value>false</boolean_value><integer_value>34</integer_value><boolean_value>true</boolean_value>
注意,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++ 中class
与struct
完全相同。所以你的意思是说使用*ADT抽象一个类。以上是关于提升结构和类的融合序列类型和名称识别的主要内容,如果未能解决你的问题,请参考以下文章