I am using a boost directed graph with a custom class as bundled property for the vertices, and want to print it in DOT format using graphviz. The class has a private variable, whose value I want to appear in the DOT file.
A sample code to demonstrate my issue:
#include <iostream>
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
class VertexClass
{
public:
VertexClass() { id = 12; }
VertexClass( int newId ) { id = newId; }
int get_id() { return id; }
void set_id( int newId ) { id = newId; }
private:
int id;
};
typedef boost::directed_graph<VertexClass, boost::no_property> Graph;
int main(int,char*[])
{
Graph g;
Graph::vertex_descriptor v0 = g.add_vertex(3);
Graph::vertex_descriptor v1 = g.add_vertex(5);
Graph::vertex_descriptor v2 = g.add_vertex(6);
boost::add_edge(v0,v1,g);
boost::add_edge(v1,v2,g);
//boost::write_graphviz(std::cout, g, ...);
return 0;
}
Desired output:
digraph G {
0[label=3];
1[label=5];
2[label=6];
0->1 ;
1->2 ;
}
(obtained by making "id" public and running below code).
Now, "id" is private, so below code (which I found reading other similar questions) won't work for me:
boost::write_graphviz(std::cout, g, boost::make_label_writer(boost::get(&VertexClass::id, g)));
I guess I have to use the accessor to get the id. After a bit of searching, I found some people suggesting using a value transforming property map (make_transform_value_property_map).
Then I found this answer. But the problem is that in that case the property is not defined as bundled, but using enum and BOOST_INSTALL_PROPERTY. So since I don't have these tags that this method provides (and it would be difficult for me to switch methods, since my actual code is more complex), it doesn't work for me (or at least I don't know how to make it do so).
Next, after reading this answer I tried the following:
boost::write_graphviz(std::cout, g, boost::make_label_writer(boost::make_transform_value_property_map(&VertexClass::get_id, boost::get(boost::vertex_bundle, g))));
but I get the following error (full output below):
$ g++ -Wall -std=c++11 main.cpp
In file included from /usr/local/include/boost/graph/directed_graph.hpp:13:0,
from main.cpp:2:
/usr/local/include/boost/property_map/transform_value_property_map.hpp: In instantiation of ‘boost::transform_value_property_map<Func, PM, Ret>::reference boost::transform_value_property_map<Func, PM, Ret>::operator[](const key_type&) const [with Func = int (VertexClass::*)(); PM = boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>; Ret = int; boost::transform_value_property_map<Func, PM, Ret>::reference = int; boost::transform_value_property_map<Func, PM, Ret>::key_type = void*]’:
/usr/local/include/boost/property_map/property_map.hpp:303:54: required from ‘Reference boost::get(const boost::put_get_helper<Reference, PropertyMap>&, const K&) [with PropertyMap = boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int>; Reference = int; K = void*]’
/usr/local/include/boost/graph/graphviz.hpp:85:56: required from ‘void boost::label_writer<Name>::operator()(std::ostream&, const VertexOrEdge&) const [with VertexOrEdge = void*; Name = boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int>; std::ostream = std::basic_ostream<char>]’
/usr/local/include/boost/graph/graphviz.hpp:270:18: required from ‘void boost::write_graphviz(std::ostream&, const Graph&, VertexPropertiesWriter, EdgePropertiesWriter, GraphPropertiesWriter, VertexID, typename boost::enable_if_c<boost::is_base_and_derived<boost::vertex_list_graph_tag, typename boost::graph_traits<Graph>::traversal_category>::value, boost::graph::detail::no_parameter>::type) [with Graph = boost::directed_graph<VertexClass, boost::no_property>; VertexPropertiesWriter = boost::label_writer<boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int> >; EdgePropertiesWriter = boost::default_writer; GraphPropertiesWriter = boost::default_writer; VertexID = boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, unsigned int, const unsigned int&, boost::vertex_index_t>; std::ostream = std::basic_ostream<char>; typename boost::enable_if_c<boost::is_base_and_derived<boost::vertex_list_graph_tag, typename boost::graph_traits<Graph>::traversal_category>::value, boost::graph::detail::no_parameter>::type = boost::graph::detail::no_parameter]’
/usr/local/include/boost/graph/graphviz.hpp:290:63: required from ‘void boost::write_graphviz(std::ostream&, const Graph&, VertexPropertiesWriter, EdgePropertiesWriter, GraphPropertiesWriter, typename boost::enable_if_c<boost::is_base_and_derived<boost::vertex_list_graph_tag, typename boost::graph_traits<Graph>::traversal_category>::value, boost::graph::detail::no_parameter>::type) [with Graph = boost::directed_graph<VertexClass, boost::no_property>; VertexPropertiesWriter = boost::label_writer<boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int> >; EdgePropertiesWriter = boost::default_writer; GraphPropertiesWriter = boost::default_writer; std::ostream = std::basic_ostream<char>; typename boost::enable_if_c<boost::is_base_and_derived<boost::vertex_list_graph_tag, typename boost::graph_traits<Graph>::traversal_category>::value, boost::graph::detail::no_parameter>::type = boost::graph::detail::no_parameter]’
/usr/local/include/boost/graph/graphviz.hpp:309:38: required from ‘void boost::write_graphviz(std::ostream&, const Graph&, VertexWriter, typename boost::enable_if_c<boost::is_base_and_derived<boost::vertex_list_graph_tag, typename boost::graph_traits<Graph>::traversal_category>::value, boost::graph::detail::no_parameter>::type) [with Graph = boost::directed_graph<VertexClass, boost::no_property>; VertexWriter = boost::label_writer<boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int> >; std::ostream = std::basic_ostream<char>; typename boost::enable_if_c<boost::is_base_and_derived<boost::vertex_list_graph_tag, typename boost::graph_traits<Graph>::traversal_category>::value, boost::graph::detail::no_parameter>::type = boost::graph::detail::no_parameter]’
main.cpp:30:166: required from here
/usr/local/include/boost/property_map/transform_value_property_map.hpp:45:24: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘((const boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int>*)this)->boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int>::f (...)’, e.g. ‘(... ->* ((const boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int>*)this)->boost::transform_value_property_map<int (VertexClass::*)(), boost::adj_list_vertex_property_map<boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, boost::property<boost::vertex_index_t, unsigned int, VertexClass>, boost::property<boost::edge_index_t, unsigned int, boost::no_property>, boost::no_property, boost::listS>, VertexClass, VertexClass&, boost::vertex_bundle_t>, int>::f) (...)’
return f(get(pm, k));
Every question/answer I've seen is about one of the two cases mentioned above (bundled properties with public variables (e.g. structs), or old-fashioned property declaration).
I am a newbie in boost graph library, so I guess I may have missed something. But I can't figure out a solution (and got really lost in boost's documentation), so any help would be much appreciated.
The include for transform_value_property_map
indicates to me that you have been close to a solution. Here it is:
boost::dynamic_properties dp;
First, let's state that you want de intrinsic vertex index for the graphviz node ids:
dp.property("node_id", get(boost::vertex_index, g));
Now we want to do stuff with the bundle, so let's grab the property-map for the whole bundle:
auto vbundle = get(boost::vertex_bundle, g);
But we can't use it directly. We need to transform it:
dp.property("label",
boost::make_transform_value_property_map([](VertexClass const& vd) {
return vd.get_id();
}, vbundle));
Note! I've made
get_id() const
constant to make this compile
Now write:
boost::write_graphviz_dp(std::cout, g, dp);
Result:
digraph G {
0 [label=3];
1 [label=5];
2 [label=6];
0->1 ;
1->2 ;
}
In this case, you could get away with simpler:
dp.property("label",
boost::make_transform_value_property_map(std::mem_fn(&VertexClass::get_id), vbundle));
instead the more flexible lambda
You seem to have fallen into the trap of requiring quasi-classes (PDF) where they add no value. Simplify:
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/graphviz.hpp>
#include <iostream>
struct VertexProps { int id; };
typedef boost::directed_graph<VertexProps> Graph;
int main() {
Graph g;
auto v1 = add_vertex({5}, g);
add_edge(add_vertex({3}, g), v1, g);
add_edge(v1, add_vertex({6}, g), g);
boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("label", get(&VertexProps::id, g));
write_graphviz_dp(std::cout, g, dp);
}
As you can see now the whole thing shrinks to 20 lines of code with first-class support for your vertex properties.