使用 boost::fusion 迭代时将漂亮的打印机绑定到 boost::phoenix actors
Binding a pretty-printer to boost::phoenix actors when iterating with boost::fusion
此问题是 的后续问题,其中已接受的解决方案有效。
现在,我不仅要将(原始)值添加到 属性-map,还要使用漂亮的打印机来改进值的显示方式。如果打印的值不是微不足道的,也将使用此机制。
所以,有一些像这样的漂亮打印机:
template<typename T>
std::string prettyPrinter(const T& t);
template<>
std::string prettyPrinter(const std::string& s)
{
return "The string id: " + s;
}
template<>
std::string prettyPrinter(const int& i)
{
return "The int id: " + std::to_string(i);
}
我通过将 prettyPrinter 绑定到 boost::phoenix
actor 扩展了上一个问题的解决方案:
#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;
};
struct Edge {
int id;
};
BOOST_FUSION_ADAPT_STRUCT(Vertex, id)
BOOST_FUSION_ADAPT_STRUCT(Edge, id)
template <typename Tag, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
using namespace boost;
using Bundle = typename property_map<T_Graph, Tag>::type;
using T_Seq = typename 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();
using TempType = typename fusion::result_of::value_at<T_Seq, decltype(i)>::type;
//
// Interesting part starts here:
//
dp.property(
name,
make_transform_value_property_map(
phoenix::bind(
prettyPrinter<TempType>,
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);
}
我现在正在寻找更优雅的解决方案的可能性,因为@sehe 在评论中指出,使用 phoenix::bind
可能不是这里的最佳选择。
您显示的代码中存在一些随机错误。 None 的 prettyPrinter
重载将编译。没有 Seq
这样的东西。您为不存在的成员调整结构。
撇开所有这些不谈,你在这里跑的是次优线路:函数模板和特化不能很好地混合 (Why not specialize function templates, [HSutter,2001]¹)。
您似乎有意硬编码您的类型以及漂亮的打印逻辑。
真言:
Type deduction and ADL are your friends for extensible mechanisms.
我会把漂亮的打印界面大致写成这样:
#include <string>
namespace pretty_printing
{
namespace default_impl {
std::string do_pretty_print(const std::string& s) {
return "The string id: " + s;
}
std::string do_pretty_print(const int i) {
return "The int id: " + std::to_string(i);
}
}
struct pretty_print_f {
using result_type = std::string;
template <typename T> result_type operator()(T&& v) const {
using namespace default_impl; // enable ADL
return do_pretty_print(std::forward<T>(v));
}
};
}
您现在可以让您的 do_pretty_print
过载
- 以您想要的任何方式(包括提供通用重载)
- 在安全的地方,例如作为您类型的
friend
成员,或作为您类型的关联命名空间中的(模板)函数
他们将 "magically" 在 POI 被选中。
现在,我建议使用 boost::phoenix::function<>
而不是 boost::phoenix::bind
来获得更清晰的调用站点:
auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
px::function<pretty_printing::pretty_print_f> pretty_print;
dp.property(
name,
make_transform_value_property_map(pretty_print(px::at_c<i>(arg1)), bundle)
);
简化的关键是让编译器做它最擅长的事情:使用推导的参数类型进行重载解析。
完整演示
#include <string>
namespace pretty_printing
{
namespace default_impl {
std::string do_pretty_print(const std::string& s) {
return "The string id: " + s;
}
std::string do_pretty_print(const int i) {
return "The int id: " + std::to_string(i);
}
}
struct pretty_print_f {
using result_type = std::string;
template <typename T> result_type operator()(T&& v) const {
using namespace default_impl;
return do_pretty_print(std::forward<T>(v));
}
};
}
#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 {
int more;
std::string 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;
namespace px = boost::phoenix;
using namespace px::arg_names;
using Bundle = typename property_map<T_Graph, Tag>::type;
using T_Seq = typename 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();
px::function<pretty_printing::pretty_print_f> pretty_print;
dp.property(
name,
make_transform_value_property_map(pretty_print(px::at_c<i>(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);
}
¹ 另见 GotW#49 http://www.gotw.ca/gotw/049.htm and Template Specialization VS Function Overloading 例如
此问题是
现在,我不仅要将(原始)值添加到 属性-map,还要使用漂亮的打印机来改进值的显示方式。如果打印的值不是微不足道的,也将使用此机制。
所以,有一些像这样的漂亮打印机:
template<typename T>
std::string prettyPrinter(const T& t);
template<>
std::string prettyPrinter(const std::string& s)
{
return "The string id: " + s;
}
template<>
std::string prettyPrinter(const int& i)
{
return "The int id: " + std::to_string(i);
}
我通过将 prettyPrinter 绑定到 boost::phoenix
actor 扩展了上一个问题的解决方案:
#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;
};
struct Edge {
int id;
};
BOOST_FUSION_ADAPT_STRUCT(Vertex, id)
BOOST_FUSION_ADAPT_STRUCT(Edge, id)
template <typename Tag, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
using namespace boost;
using Bundle = typename property_map<T_Graph, Tag>::type;
using T_Seq = typename 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();
using TempType = typename fusion::result_of::value_at<T_Seq, decltype(i)>::type;
//
// Interesting part starts here:
//
dp.property(
name,
make_transform_value_property_map(
phoenix::bind(
prettyPrinter<TempType>,
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);
}
我现在正在寻找更优雅的解决方案的可能性,因为@sehe 在评论中指出,使用 phoenix::bind
可能不是这里的最佳选择。
您显示的代码中存在一些随机错误。 None 的 prettyPrinter
重载将编译。没有 Seq
这样的东西。您为不存在的成员调整结构。
撇开所有这些不谈,你在这里跑的是次优线路:函数模板和特化不能很好地混合 (Why not specialize function templates, [HSutter,2001]¹)。
您似乎有意硬编码您的类型以及漂亮的打印逻辑。
真言:
Type deduction and ADL are your friends for extensible mechanisms.
我会把漂亮的打印界面大致写成这样:
#include <string>
namespace pretty_printing
{
namespace default_impl {
std::string do_pretty_print(const std::string& s) {
return "The string id: " + s;
}
std::string do_pretty_print(const int i) {
return "The int id: " + std::to_string(i);
}
}
struct pretty_print_f {
using result_type = std::string;
template <typename T> result_type operator()(T&& v) const {
using namespace default_impl; // enable ADL
return do_pretty_print(std::forward<T>(v));
}
};
}
您现在可以让您的 do_pretty_print
- 以您想要的任何方式(包括提供通用重载)
- 在安全的地方,例如作为您类型的
friend
成员,或作为您类型的关联命名空间中的(模板)函数
他们将 "magically" 在 POI 被选中。
现在,我建议使用 boost::phoenix::function<>
而不是 boost::phoenix::bind
来获得更清晰的调用站点:
auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
px::function<pretty_printing::pretty_print_f> pretty_print;
dp.property(
name,
make_transform_value_property_map(pretty_print(px::at_c<i>(arg1)), bundle)
);
简化的关键是让编译器做它最擅长的事情:使用推导的参数类型进行重载解析。
完整演示
#include <string>
namespace pretty_printing
{
namespace default_impl {
std::string do_pretty_print(const std::string& s) {
return "The string id: " + s;
}
std::string do_pretty_print(const int i) {
return "The int id: " + std::to_string(i);
}
}
struct pretty_print_f {
using result_type = std::string;
template <typename T> result_type operator()(T&& v) const {
using namespace default_impl;
return do_pretty_print(std::forward<T>(v));
}
};
}
#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 {
int more;
std::string 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;
namespace px = boost::phoenix;
using namespace px::arg_names;
using Bundle = typename property_map<T_Graph, Tag>::type;
using T_Seq = typename 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();
px::function<pretty_printing::pretty_print_f> pretty_print;
dp.property(
name,
make_transform_value_property_map(pretty_print(px::at_c<i>(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);
}
¹ 另见 GotW#49 http://www.gotw.ca/gotw/049.htm and Template Specialization VS Function Overloading 例如