I have a boost::graph that uses bundled properties like the following:
struct Vertex
{
std::string id;
};
If I want to use this information in boost::dynamic_properties
(e.g. for printing in graphml-format), I can use something like that:
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)
)
);
}
Since the bundled property might change in the future, I want to be generic about the creation of the dynamic_properties
. Therefore, I use 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);
}
My problem is, that I can't find a way to express the line &T_Seq::id
in a generic way. I have been looking into fusion::extension::struct_member_name
, but was not successful.
I search for either a generic way to replace the problematic line or a different approach entirely to iterate over the members of Vertex
.
Regardless of the awesome existing answer, I always hesitate to use macros.
In this case I noticed everything became difficult because of the boost::property_map<Graph, Tag>
interface. I reckoned, you could just use the vertex_bundle_t
instead.
Here's a simple demo using no macros at all, and that works for the vertex and edge bundles. (You could remove the debug output and add the pretty print hook back in).
#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);
}
Prints
Adding id
Adding numeric_value
Adding more
Adding awesome_sauce