I cannot for the life of me understand how to setup read_graphml
correctly.
Ommiting obvious file IO, so far I have:
struct NodeProperties {
std::string d{};
std::string e{};
std::string f{};
};
struct EdgeProperties {};
struct GraphProperties {
std::string a{};
std::string b{};
std::string c{};
};
usingDiGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, NodeProperties, EdgeProperties, GraphProperties>;
DiGraph myTree;
std::map<std::string,std::string> attribute_name2name;
//attribute_name2name.insert( make_pair(std::string("a"), std::string("a") ) ); // doesnt help
boost::associative_property_map<std::map<std::string,std::string>> graphname_map( attribute_name2name );
boost::dynamic_properties treeProperties;
treeProperties.property( "a", graphname_map );
try {
read_graphml(graphmlFile, tree, treeProperties);
}
catch ( std::exception &ex ) {
std::cerr << "Exception caught from read_graphml: " << ex.what() << "\n";
}
boost::print_graph(tree, "tree");
The file looks more or less like this (sorry if i made a typo, i need to manually write it because reasons):
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="key0" for="graph" attr.name="a" attr.type="string" />
<key id="key1" for="graph" attr.name="b" attr.type="string" />
<key id="key2" for="graph" attr.name="c" attr.type="string" />
<key id="key3" for="node" attr.name="d" attr.type="string" />
<key id="key4" for="node" attr.name="e" attr.type="string" />
<key id="key5" for="node" attr.name="f" attr.type="string" />
<graph id="G" edgefault="directed" parse.nodeis="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<data key="key0">foo</data>
<data key="key1">foo</data>
<data key="key2">foo</data>
<node id="n0">
<data key="key3">bar</data>
<data key="key4">bar</data>
<data key="key5">bar</data>
</node>
<node id="n1">
<data key="key3">bar</data>
<data key="key4">bar</data>
<data key="key5">bar</data>
</node>
<node id="n2">
<data key="key3">bar</data>
<data key="key4">bar</data>
<data key="key5">bar</data>
</node>
<edge id="e0" source="n0" target="n1"> </edge>
<edge id="e0" source="n0" target="n2"> </edge>
</graph>
</graphml>
read_graphml
throws exception:
Property not found: a
HOWEVER, if the fourth argument, optional argument of read_graphml
function is set to 'G', no exception happens - however print tree doesn't print anything, not even "tree"
Note that I do not need the code to be generic. I KNOW what the node and graph properties are, and I know they wont change. I can statically bake them into the source.
Note that I did not use ignore_other_properties
because I want to read them all - and anyway here it crashes on first property in the file.
Related questions dont help me or contain no answer, but may be useful:
how to read graph-domain attributes with boost::read_graphml?
Boost Read_graphml doesn't read xml properly it gives all the vertices but they are empty
I tried doing it like here but the compiler cannot resolve boost::get(&GraphProperties::a,tree)
, example on Coliru:
https://coliru.stacked-crooked.com/a/ef8a74489dc1f53b
EDIT: My hunch is that the way of going about it presented on Coliru is probably a better way - since many people asking on SO seem to use it - but that is only if the compiler error can be fixed.
You are confusing property maps with maps. Instead of a single map (this will only give you 1 element per key anyways), you want a "mapping" (technically, a PropertyMap) that maps descriptors to corresponding values.
I'd expect this:
boost::dynamic_properties dp;
dp.property("d", get(&NodeProperties::d, myTree));
dp.property("e", get(&NodeProperties::e, myTree));
dp.property("f", get(&NodeProperties::f, myTree));
Now, the graph properties are a different beast. You need to map a reference to the the "surrogate key" of type DiGraph*
:
auto graph_prop = [&g](auto member) {
return boost::make_function_property_map<DiGraph*>(
[&g, member](DiGraph*) -> decltype(auto) { return g[boost::graph_bundle].*member; });
};
dp.property("a", graph_prop(&GraphProperties::a));
dp.property("b", graph_prop(&GraphProperties::b));
dp.property("c", graph_prop(&GraphProperties::c));
There might be a better way to map those, but I'm not aware of it.
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
struct NodeProperties {
std::string d, e, f;
friend std::ostream& operator<<(std::ostream& os, NodeProperties const& np) {
return os << "{d:" << quoted(np.d) << ", e:" << quoted(np.e) << ", f:" << quoted(np.f) << "}";
}
};
struct EdgeProperties {};
struct GraphProperties {
std::string a, b, c;
friend std::ostream& operator<<(std::ostream& os, GraphProperties const& gp) {
return os << "{a:" << quoted(gp.a) << ", b:" << quoted(gp.b) << ", c:" << quoted(gp.c) << "}";
}
};
using DiGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, NodeProperties,
EdgeProperties, GraphProperties>;
int main() try {
DiGraph g;
/*
*auto n0 = add_vertex({"bar", "bar", "bar"}, g);
*auto n1 = add_vertex({"baz", "baz", "baz"}, g);
*auto n2 = add_vertex({"qux", "qux", "qux"}, g);
*add_edge(n0, n1, g);
*add_edge(n0, n2, g);
*/
boost::dynamic_properties dp;
dp.property("d", get(&NodeProperties::d, g));
dp.property("e", get(&NodeProperties::e, g));
dp.property("f", get(&NodeProperties::f, g));
auto graph_prop = [&g](auto member) {
return boost::make_function_property_map<DiGraph*>(
[&g, member](DiGraph*) -> decltype(auto) { return g[boost::graph_bundle].*member; });
};
dp.property("a", graph_prop(&GraphProperties::a));
dp.property("b", graph_prop(&GraphProperties::b));
dp.property("c", graph_prop(&GraphProperties::c));
{
std::ifstream ifs("input.xml");
read_graphml(ifs, g, dp);
}
std::cout << "Read graph with " << num_vertices(g) << " vertices and " << num_edges(g) << " edges\n";
std::cout << "Graph properties: " << g[boost::graph_bundle] << "\n";
print_graph(g, get(&NodeProperties::d, g));
print_graph(g, get(boost::vertex_bundle, g));
{
std::ofstream ofs("output.xml");
write_graphml(ofs, g, dp);
}
} catch (std::exception const& ex) {
std::cerr << "Exception caught from read_graphml: " << ex.what() << "\n";
}
Output
Read graph with 3 vertices and 2 edges
Graph properties: {a:"graph_a", b:"graph_b", c:"graph_c"}
bar_d --> baz_d qux_d
baz_d -->
qux_d -->
{d:"bar_d", e:"bar_e", f:"bar_f"} --> {d:"baz_d", e:"baz_e", f:"baz_f"} {d:"qux_d", e:"qux_e", f:"qux_f"}
{d:"baz_d", e:"baz_e", f:"baz_f"} -->
{d:"qux_d", e:"qux_e", f:"qux_f"} -->
And roundtripped output.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="key0" for="graph" attr.name="a" attr.type="string" />
<key id="key1" for="graph" attr.name="b" attr.type="string" />
<key id="key2" for="graph" attr.name="c" attr.type="string" />
<key id="key3" for="node" attr.name="d" attr.type="string" />
<key id="key4" for="node" attr.name="e" attr.type="string" />
<key id="key5" for="node" attr.name="f" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<data key="key0">graph_a</data>
<data key="key1">graph_b</data>
<data key="key2">graph_c</data>
<node id="n0">
<data key="key3">bar_d</data>
<data key="key4">bar_e</data>
<data key="key5">bar_f</data>
</node>
<node id="n1">
<data key="key3">baz_d</data>
<data key="key4">baz_e</data>
<data key="key5">baz_f</data>
</node>
<node id="n2">
<data key="key3">qux_d</data>
<data key="key4">qux_e</data>
<data key="key5">qux_f</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n0" target="n2">
</edge>
</graph>
</graphml>