This question is a follow-up to Pointers to class members when iterating with boost::fusion, where the accepted solution works.
Now, I want not only to add the (primitive) values to the property-map, but use a pretty-printer to improve how the values are displayed. This mechanism will also be used in case the values are not trivial to print.
So, there is some pretty-printer like this:
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);
}
and I expanded the solution from the previous question by binding the prettyPrinter to the 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);
}
What I am now looking for is the possibility of a more elegant solution, since @sehe pointed out int a comment, that using phoenix::bind
might not be the optimal thing to do here.
There are some random errors in the code you show. None of the
prettyPrinter
overloads would compile. There's no such thing asSeq
. You adapt the structs for members that don't exist.All these things aside, you are racing a suboptimal line here: function templates and specializations don't mix well (Why not specialize function templates, [HSutter,2001]¹).
You seem intent on hard-coding your types as well as the pretty-print logic.
Mantra:
I'd write the pretty print interface roughly like:
You can now overload your
do_pretty_print
friend
members of your types, or as (template) functions in the associated namespace(s) of your typesThey will "magically" be picked at the POI.
Now, I suggest using
boost::phoenix::function<>
instead ofboost::phoenix::bind
to arrive at a much cleaner call-site:Key to the simplification is to let the compiler do what it does best: overload resolution with deduced argument types.
Full Demo
Live On Coliru
¹ See also GotW#49 http://www.gotw.ca/gotw/049.htm and Template Specialization VS Function Overloading e.g.