使用 boost::fusion 迭代时指向 class 成员的指针
Pointers to class members when iterating with boost::fusion
我有一个 boost::graph 使用如下捆绑属性:
struct Vertex
{
std::string id;
};
如果我想在 boost::dynamic_properties
中使用此信息(例如以 graphml 格式打印),我可以使用类似的东西:
template<typename T>
std::string myPrettyPrinter(const T& t);
int main()
{
using namespace boost;
MyGraph g;
dynamic_properties dp;
dp.property("id",
make_transform_value_property_map(
& myPrettyPrinter<std::string>,
get(&Vertex::id, g)
)
);
}
由于捆绑的 属性 将来可能会发生变化,因此我想对 dynamic_properties
的创建进行通用化。因此,我使用 boost::fusion
struct Vertex
{
std::string id;
};
BOOST_FUSION_ADAPT_STRUCT(
Vertex,
id
)
template<typename T>
std::string myPrettyPrinter(const T& t);
template <typename T_Seq, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
using namespace boost;
using Indices = mpl::range_c<
unsigned,
0,
fusion::result_of::size<T_Seq>::value
>;
fusion::for_each(
Indices(),
[&](auto i)
{
using I = decltype(i);
dp.property(
fusion::extension::struct_member_name<T_Seq, i>::call(),
make_transform_value_property_map(
& myPrettyPrinter<
typename fusion::result_of::value_at<T_Seq, I>::type
>,
get(
// This works but is not generic,
// since it relies on the specific
// member name "id":
& T_Seq::id,
g
)
)
);
}
);
}
int main()
{
MyGraph g;
boost::dynamic_properties dp;
member_iterator<Vertex>(dp, g);
}
我的问题是,我找不到以通用方式表达行 &T_Seq::id
的方法。我一直在研究 fusion::extension::struct_member_name
,但没有成功。
我寻找一种通用方法来替换有问题的行,或者寻找一种完全不同的方法来遍历 Vertex
的成员。
据我所知,您无法从 BOOST_FUSION_ADAPT_STRUCT
内部存储的信息中获取指向成员的指针(正如您发现的那样,您可以获取成员类型或名称)。解决此问题的一种可能方法是创建一个调用 BOOST_FUSION_ADAPT_STRUCT
的宏,然后存储附加信息。我选择这样存储它:
template<>
struct pointer_to_member_N<Vertex,0>
{
static constexpr std::string Vertex::* value = &Vertex::id;
};
为了生成该结构,我使用了以下宏:
#define YOUR_NS_SAVE_MEMBERPTR(STRUCT_NAME,MEMBERS) \
namespace your_ns { \
BOOST_PP_SEQ_FOR_EACH_I(CREATE_POINTER_TO_MEMBER_TRAIT,STRUCT_NAME,BOOST_PP_CAT(YOUR_NS_SAVE_MEMBERPTR_FILLER_0 MEMBERS,_END)) \
}
此宏采用结构名称和一对序列 (type,name)
,并简单地打开命名空间 your_ns
并调用 CREATE_POINTER_TO_MEMBER_TRAIT
,每对传递 STRUCT_NAME
作为数据。
#define CREATE_POINTER_TO_MEMBER_TRAIT(R,STRUCT_NAME,INDEX,TYPE_AND_NAME) \
template <> struct pointer_to_member_N<STRUCT_NAME, INDEX>{ static constexpr BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) STRUCT_NAME::* value = &STRUCT_NAME::BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME); };
这是真正创造特质的那个。它需要一个参数 R
(我不知道它做了什么)、你正在调整的结构的名称、当前成员的索引和一对 (type,name)
。它使用 BOOST_PP_TUPLE_ELEM(2,N,TYPE_AND_NAME)
获取成员的类型或名称。
#include <iostream>
#include <string>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/mpl.hpp>
#include <boost/mpl/range_c.hpp>
#include <cstddef>
namespace your_ns
{
template <typename StructName, int N>
struct pointer_to_member_N;
}
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_0(X, Y) \
((X, Y)) YOUR_NS_SAVE_MEMBERPTR_FILLER_1
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_1(X, Y) \
((X, Y)) YOUR_NS_SAVE_MEMBERPTR_FILLER_0
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_0_END
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_1_END
#define CREATE_POINTER_TO_MEMBER_TRAIT(R,STRUCT_NAME,INDEX,TYPE_AND_NAME) \
template <> struct pointer_to_member_N<STRUCT_NAME, INDEX>{ static constexpr BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) STRUCT_NAME::* value = &STRUCT_NAME::BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME); };
#define YOUR_NS_SAVE_MEMBERPTR(STRUCT_NAME,MEMBERS) \
namespace your_ns { \
BOOST_PP_SEQ_FOR_EACH_I(CREATE_POINTER_TO_MEMBER_TRAIT,STRUCT_NAME,BOOST_PP_CAT(YOUR_NS_SAVE_MEMBERPTR_FILLER_0 MEMBERS,_END)) \
}
#define ADAPT_STRUCT_AND_SAVE_MEMBERPTR(TYPE,MEMBERS) BOOST_FUSION_ADAPT_STRUCT(TYPE,MEMBERS) YOUR_NS_SAVE_MEMBERPTR(TYPE,MEMBERS)
struct Vertex {
std::string id;
std::size_t index;
};
ADAPT_STRUCT_AND_SAVE_MEMBERPTR(
Vertex,
(std::string, id)
(std::size_t, index)
)
int main() {
Vertex v;
v.id="A";
v.index=0;
std::cout << std::mem_fn(your_ns::pointer_to_member_N<Vertex,0>::value)(v) << std::endl;
std::cout << v.*your_ns::pointer_to_member_N<Vertex,1>::value << std::endl;
}
不管现有的答案有多棒,我总是对使用宏犹豫不决。
在这种情况下,我注意到由于 boost::property_map<Graph, Tag>
界面,一切都变得困难了。我估计,您可以改用 vertex_bundle_t
。
这是一个完全不使用宏的简单演示,适用于顶点和边束。 (您可以删除调试输出并重新添加漂亮的打印挂钩)。
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/find.hpp>
#include <boost/phoenix/fusion/at.hpp>
#include <boost/phoenix.hpp>
#include <boost/mpl/range_c.hpp>
#include <iostream>
struct Vertex {
std::string id;
int numeric_value;
};
struct Edge {
std::string more;
int awesome_sauce;
};
BOOST_FUSION_ADAPT_STRUCT(Vertex, id, numeric_value)
BOOST_FUSION_ADAPT_STRUCT(Edge, more, awesome_sauce)
template <typename Tag, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
using namespace boost;
using Bundle = typename boost::property_map<T_Graph, Tag>::type;
using T_Seq = typename boost::property_traits<Bundle>::value_type;
using Indices = mpl::range_c<unsigned, 0, fusion::result_of::size<T_Seq>::value>;
fusion::for_each(
Indices{},
[&, bundle=get(Tag{}, g)](auto i) {
auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
std::cout << "Adding " << name << "\n";
dp.property(
name,
make_transform_value_property_map(phoenix::at_c<i>(phoenix::arg_names::arg1), bundle)
);
}
);
}
using MyGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, Vertex, Edge>;
int main()
{
MyGraph g;
boost::dynamic_properties dp;
member_iterator<boost::vertex_bundle_t>(dp, g);
member_iterator<boost::edge_bundle_t>(dp, g);
}
版画
Adding id
Adding numeric_value
Adding more
Adding awesome_sauce
我有一个 boost::graph 使用如下捆绑属性:
struct Vertex
{
std::string id;
};
如果我想在 boost::dynamic_properties
中使用此信息(例如以 graphml 格式打印),我可以使用类似的东西:
template<typename T>
std::string myPrettyPrinter(const T& t);
int main()
{
using namespace boost;
MyGraph g;
dynamic_properties dp;
dp.property("id",
make_transform_value_property_map(
& myPrettyPrinter<std::string>,
get(&Vertex::id, g)
)
);
}
由于捆绑的 属性 将来可能会发生变化,因此我想对 dynamic_properties
的创建进行通用化。因此,我使用 boost::fusion
struct Vertex
{
std::string id;
};
BOOST_FUSION_ADAPT_STRUCT(
Vertex,
id
)
template<typename T>
std::string myPrettyPrinter(const T& t);
template <typename T_Seq, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
using namespace boost;
using Indices = mpl::range_c<
unsigned,
0,
fusion::result_of::size<T_Seq>::value
>;
fusion::for_each(
Indices(),
[&](auto i)
{
using I = decltype(i);
dp.property(
fusion::extension::struct_member_name<T_Seq, i>::call(),
make_transform_value_property_map(
& myPrettyPrinter<
typename fusion::result_of::value_at<T_Seq, I>::type
>,
get(
// This works but is not generic,
// since it relies on the specific
// member name "id":
& T_Seq::id,
g
)
)
);
}
);
}
int main()
{
MyGraph g;
boost::dynamic_properties dp;
member_iterator<Vertex>(dp, g);
}
我的问题是,我找不到以通用方式表达行 &T_Seq::id
的方法。我一直在研究 fusion::extension::struct_member_name
,但没有成功。
我寻找一种通用方法来替换有问题的行,或者寻找一种完全不同的方法来遍历 Vertex
的成员。
据我所知,您无法从 BOOST_FUSION_ADAPT_STRUCT
内部存储的信息中获取指向成员的指针(正如您发现的那样,您可以获取成员类型或名称)。解决此问题的一种可能方法是创建一个调用 BOOST_FUSION_ADAPT_STRUCT
的宏,然后存储附加信息。我选择这样存储它:
template<>
struct pointer_to_member_N<Vertex,0>
{
static constexpr std::string Vertex::* value = &Vertex::id;
};
为了生成该结构,我使用了以下宏:
#define YOUR_NS_SAVE_MEMBERPTR(STRUCT_NAME,MEMBERS) \
namespace your_ns { \
BOOST_PP_SEQ_FOR_EACH_I(CREATE_POINTER_TO_MEMBER_TRAIT,STRUCT_NAME,BOOST_PP_CAT(YOUR_NS_SAVE_MEMBERPTR_FILLER_0 MEMBERS,_END)) \
}
此宏采用结构名称和一对序列 (type,name)
,并简单地打开命名空间 your_ns
并调用 CREATE_POINTER_TO_MEMBER_TRAIT
,每对传递 STRUCT_NAME
作为数据。
#define CREATE_POINTER_TO_MEMBER_TRAIT(R,STRUCT_NAME,INDEX,TYPE_AND_NAME) \
template <> struct pointer_to_member_N<STRUCT_NAME, INDEX>{ static constexpr BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) STRUCT_NAME::* value = &STRUCT_NAME::BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME); };
这是真正创造特质的那个。它需要一个参数 R
(我不知道它做了什么)、你正在调整的结构的名称、当前成员的索引和一对 (type,name)
。它使用 BOOST_PP_TUPLE_ELEM(2,N,TYPE_AND_NAME)
获取成员的类型或名称。
#include <iostream>
#include <string>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/mpl.hpp>
#include <boost/mpl/range_c.hpp>
#include <cstddef>
namespace your_ns
{
template <typename StructName, int N>
struct pointer_to_member_N;
}
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_0(X, Y) \
((X, Y)) YOUR_NS_SAVE_MEMBERPTR_FILLER_1
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_1(X, Y) \
((X, Y)) YOUR_NS_SAVE_MEMBERPTR_FILLER_0
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_0_END
#define YOUR_NS_SAVE_MEMBERPTR_FILLER_1_END
#define CREATE_POINTER_TO_MEMBER_TRAIT(R,STRUCT_NAME,INDEX,TYPE_AND_NAME) \
template <> struct pointer_to_member_N<STRUCT_NAME, INDEX>{ static constexpr BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) STRUCT_NAME::* value = &STRUCT_NAME::BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME); };
#define YOUR_NS_SAVE_MEMBERPTR(STRUCT_NAME,MEMBERS) \
namespace your_ns { \
BOOST_PP_SEQ_FOR_EACH_I(CREATE_POINTER_TO_MEMBER_TRAIT,STRUCT_NAME,BOOST_PP_CAT(YOUR_NS_SAVE_MEMBERPTR_FILLER_0 MEMBERS,_END)) \
}
#define ADAPT_STRUCT_AND_SAVE_MEMBERPTR(TYPE,MEMBERS) BOOST_FUSION_ADAPT_STRUCT(TYPE,MEMBERS) YOUR_NS_SAVE_MEMBERPTR(TYPE,MEMBERS)
struct Vertex {
std::string id;
std::size_t index;
};
ADAPT_STRUCT_AND_SAVE_MEMBERPTR(
Vertex,
(std::string, id)
(std::size_t, index)
)
int main() {
Vertex v;
v.id="A";
v.index=0;
std::cout << std::mem_fn(your_ns::pointer_to_member_N<Vertex,0>::value)(v) << std::endl;
std::cout << v.*your_ns::pointer_to_member_N<Vertex,1>::value << std::endl;
}
不管现有的答案有多棒,我总是对使用宏犹豫不决。
在这种情况下,我注意到由于 boost::property_map<Graph, Tag>
界面,一切都变得困难了。我估计,您可以改用 vertex_bundle_t
。
这是一个完全不使用宏的简单演示,适用于顶点和边束。 (您可以删除调试输出并重新添加漂亮的打印挂钩)。
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/find.hpp>
#include <boost/phoenix/fusion/at.hpp>
#include <boost/phoenix.hpp>
#include <boost/mpl/range_c.hpp>
#include <iostream>
struct Vertex {
std::string id;
int numeric_value;
};
struct Edge {
std::string more;
int awesome_sauce;
};
BOOST_FUSION_ADAPT_STRUCT(Vertex, id, numeric_value)
BOOST_FUSION_ADAPT_STRUCT(Edge, more, awesome_sauce)
template <typename Tag, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
using namespace boost;
using Bundle = typename boost::property_map<T_Graph, Tag>::type;
using T_Seq = typename boost::property_traits<Bundle>::value_type;
using Indices = mpl::range_c<unsigned, 0, fusion::result_of::size<T_Seq>::value>;
fusion::for_each(
Indices{},
[&, bundle=get(Tag{}, g)](auto i) {
auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
std::cout << "Adding " << name << "\n";
dp.property(
name,
make_transform_value_property_map(phoenix::at_c<i>(phoenix::arg_names::arg1), bundle)
);
}
);
}
using MyGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, Vertex, Edge>;
int main()
{
MyGraph g;
boost::dynamic_properties dp;
member_iterator<boost::vertex_bundle_t>(dp, g);
member_iterator<boost::edge_bundle_t>(dp, g);
}
版画
Adding id
Adding numeric_value
Adding more
Adding awesome_sauce