boost graph read_graphml read all arbitrary dynamic properties - c++

What I want to do
Read a graphml file with all node and edge properties that exist in the file (arbitraty in type and count)
Operate on the graph
Add additional properties
Write the Graph to a graphml file again
What I have
Not much, but from what I gathered from other posts and the boost documentation:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <fstream>
int main(int argc, char const *argv[]) {
auto filename = argv[1];
std::ifstream input_stream;
input_stream.open(filename, std::ifstream::in);
boost::adjacency_list<boost::setS, boost::setS> graph;
boost::dynamic_properties props;
boost::read_graphml(input_stream, graph, props);
input_stream.close();
// do stuff with the graph
// write it out to a file again
return 0;
}
What's wrong
The code above exits with a property_not_found error. The reason is, that the dynamic_properties were created without a generator_fn to dynamically create a property map whenever a previously unknown property is encountered in the file (as far as I understand it, see the free put function in dynamic_property_map.hpp.
Question
How do I create a boost::dynamic_properties object that knows how to create a dynamic map for vertex / edge attributes that are found in the graphml file?
Maybe I don't even understand the use of boost::dynamic_properties correctly, if so, please point me in the right direction.

Note that the easiest is to use the builtin ignore_other_properties:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <fstream>
int main() {
boost::adjacency_list<> g;
boost::dynamic_properties props(boost::ignore_other_properties);
{
std::ifstream input_stream("input.xml");
read_graphml(input_stream, g, props);
}
write_graphml(std::cout, g, props);
}
Given input xml of
<?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="node" attr.name="Name" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">A</data>
</node>
<node id="n1">
<data key="key0">D</data>
</node>
<node id="n2">
<data key="key0">B</data>
</node>
<node id="n3">
<data key="key0">C</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>
Prints
<?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">
<graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
</node>
<node id="n1">
</node>
<node id="n2">
</node>
<node id="n3">
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>
Caveats!
Has a few caveats.
Your graph model is not equipped with storage for any properties, so I'll assume you want them to be external.
Your graph model assumes directionality and also forbids duplicate edges. This may not be what you need.
Graphviz read/write will not roundtrip. That is, comments will be gone, and edges and nodes will have arbitrarily different keys in the ML. Of course, that should in principle not matter a lot, but depending on your expectations/requirements may be a deal breaker.
Your graph model cannot even be written without an external vertex_index property, since your choice for a node-based vertex container (setS) does not have an implicit one.
How Would You Do It?
I'll drop the opinionated setS choice because of the drawbacks listed above. Note that we still assume directionality. I'll leave that for you to "fix".
A search from my existing answers shows how to use dynamic_properties with dynamic maps. Adapting to your question:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <fstream>
using Attributes = std::map<std::string, std::string>;
using DynMap = boost::shared_ptr<boost::dynamic_property_map>;
using Graph = boost::adjacency_list<>;
using V = Graph::vertex_descriptor;
using E = Graph::edge_descriptor;
static inline auto make_dyn(auto m) -> DynMap {
using DM = boost::detail::dynamic_property_map_adaptor<decltype(m)>;
auto sp = boost::make_shared<DM>(m);
return boost::static_pointer_cast<boost::dynamic_property_map>(sp);
};
int main() {
std::map<V, Attributes> va;
std::map<E, Attributes> ea;
boost::dynamic_properties props(
[vamap = boost::make_assoc_property_map(va),
eamap = boost::make_assoc_property_map(ea)] //
(std::string const& name, auto&& descriptor, auto&&) -> DynMap {
if (typeid(V) == descriptor.type()) {
return make_dyn(boost::make_function_property_map<V>(
[=](V v) -> std::string& { return vamap[v][name]; }));
} else {
return make_dyn(boost::make_function_property_map<E>(
[=](E e) -> std::string& { return eamap[e][name]; }));
}
});
Graph g;
std::ifstream input_stream("input.xml");
read_graphml(input_stream, g, props);
write_graphml(std::cout, g, props);
}
Now retains the Name attributes as expected:
<?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="node" attr.name="Name" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">A</data>
</node>
<node id="n1">
<data key="key0">D</data>
</node>
<node id="n2">
<data key="key0">B</data>
</node>
<node id="n3">
<data key="key0">C</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>
UPDATE: Strongly Typed?
In response to the comment, I added a strongly typed mapper.
It turns out pretty elegant because I kind of "flipped around" the storage making the whole variant access unnecessary (except for storage). The resulting data structures are much harder to use in your own code, that elegance is bit of lie, be warned.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/variant.hpp>
#include <fstream>
using DynMap = boost::shared_ptr<boost::dynamic_property_map>;
using Graph = boost::adjacency_list<>;
using V = Graph::vertex_descriptor;
using E = Graph::edge_descriptor;
using Name = std::string;
template <typename DescriptorT, typename T> using AttrMap = std::map<DescriptorT, T>;
template <typename DescriptorT, typename... Ts>
using VarAttr = std::map<Name, boost::variant<AttrMap<DescriptorT, Ts>...>>;
using VSupported = VarAttr<V, std::string, double>;
using ESupported = VarAttr<E, std::string, double>;
template <typename VorE, typename... Ts>
inline DynMap instantiate(VarAttr<VorE, Ts...>& supported, Name const& name,
std::type_info const& type) {
auto match_type = [&]<typename T>() -> DynMap {
if (type != typeid(T))
return nullptr; // non-matching
// emplace the appropriately typed map
using Attr = AttrMap<VorE, T>;
auto& [_, variant] = *supported.emplace(name, Attr()).first;
auto& instance = boost::get<Attr>(variant);
return boost::make_shared<
boost::detail::dynamic_property_map_adaptor<boost::associative_property_map<Attr>>>(
instance);
};
for (auto& matched : {match_type.template operator()<Ts>()...})
if (matched)
return matched;
return {};
};
int main() {
VSupported va;
ESupported ea;
boost::dynamic_properties props(
[&](std::string const& name, auto&& descriptor, auto&& value) -> DynMap {
return typeid(V) == descriptor.type() ? instantiate<V>(va, name, value.type())
: instantiate<E>(ea, name, value.type());
});
Graph g;
std::ifstream input_stream("input.xml");
read_graphml(input_stream, g, props);
props.property("Label", boost::make_function_property_map<E>([](E e) {
return boost::lexical_cast<std::string>(e);
}));
write_graphml(std::cout, g, props);
}
When adding a double attribute to edges:
<key id="key1" for="edge" attr.name="Weight" attr.type="double" />
With data like
<edge id="e0" source="n0" target="n1">
<data key="key1">12.3E-2</data>
</edge>
<edge id="e1" source="n2" target="n3">
<data key="key1">23.4E-2</data>
</edge>
Now prints output:
<edge id="e0" source="n0" target="n1">
<data key="key0">(0,1)</data>
<data key="key2">0.123</data>
</edge>
<edge id="e1" source="n2" target="n3">
<data key="key0">(2,3)</data>
<data key="key2">0.234</data>
</edge>
Showing that the attributes are not strings anymore.

Related

How to set c++ boost graphml node and edge id?

I am using the Boost graph to store a set of nodes and edges and then write it to a graphml format. Whatever I do, I cannot find a way to access or set the node id (n0, n1) or edge id (e0) attributes.
It seems to be automatically set.
Is there a way to access and set it manually ?
<?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="node" attr.name="id" attr.type="int" />
<key id="key1" for="edge" attr.name="length" attr.type="double" />
<key id="key2" for="edge" attr.name="max_speed" attr.type="double" />
<key id="key3" for="node" attr.name="name" attr.type="string" />
<key id="key4" for="edge" attr.name="name" attr.type="string" />
<key id="key5" for="edge" attr.name="source" attr.type="int" />
<key id="key6" for="node" attr.name="station" attr.type="boolean" />
<key id="key7" for="edge" attr.name="target" attr.type="int" />
<key id="key8" for="node" attr.name="theta" attr.type="double" />
<key id="key9" for="node" attr.name="x" attr.type="double" />
<key id="key10" for="node" attr.name="y" attr.type="double" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">10000</data>
<data key="key3">node1</data>
<data key="key6">0</data>
<data key="key8">0</data>
<data key="key9">6.95279e-310</data>
<data key="key10">0</data>
</node>
<node id="n1">
<data key="key0">10001</data>
<data key="key3">node1</data>
<data key="key6">0</data>
<data key="key8">0</data>
<data key="key9">6.95279e-310</data>
<data key="key10">0</data>
</node>
<edge id="e0" source="n0" target="n1">
<data key="key1">6.95279e-310</data>
<data key="key2">150</data>
<data key="key4"></data>
<data key="key5">-127787376</data>
<data key="key7">21994</data>
</edge>
</graph>
</graphml>
My graph
typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
GpNode, GpEdge>
DirectedGraph;
Where GpNode and GpEdge are custom class definitions.
Thanks in advance
write_graphml takes a dynamic_properties. Let's configure that:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
struct GpNode {
int id;
std::string name;
bool station;
double theta;
double x;
double y;
};
struct GpEdge {
double length;
double max_speed;
std::string name;
int source;
int target;
};
using DirectedGraph = boost::adjacency_list<boost::vecS, boost::vecS,
boost::directedS, GpNode, GpEdge>;
int main() {
std::ifstream ifs("input.xml");
DirectedGraph g;
auto n0 = add_vertex(
GpNode{
10000, // id
"node1", // name
0, // station
0, // theta
6.95279e-310, // x
0, // y
}, g);
auto n1 = add_vertex(
GpNode{
10001, // id
"node1", // name
0, // station
0, // theta
6.95279e-310, // x
0, // y
}, g);
/*auto e0 = */add_edge(n0, n1,
GpEdge{
6.95279e-310, // length
150, // max_speed
"", // name
-127787376, // source
21994, // target
}, g);
auto vindex = get(&GpNode::id, g);
boost::dynamic_properties dp;
//dp.property("node_id", vindex);
dp.property("id", vindex);
dp.property("name", get(&GpNode::name, g));
dp.property("station", get(&GpNode::station, g));
dp.property("theta", get(&GpNode::theta, g));
dp.property("x", get(&GpNode::x, g));
dp.property("y", get(&GpNode::y, g));
dp.property("length", get(&GpEdge::length, g));
dp.property("max_speed", get(&GpEdge::max_speed, g));
dp.property("name", get(&GpEdge::name, g));
dp.property("source", get(&GpEdge::source, g));
dp.property("target", get(&GpEdge::target, g));
boost::write_graphml(std::cout, g, dp);
}
Prints
<?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="node" attr.name="id" attr.type="int" />
<key id="key1" for="edge" attr.name="length" attr.type="double" />
<key id="key2" for="edge" attr.name="max_speed" attr.type="double" />
<key id="key3" for="node" attr.name="name" attr.type="string" />
<key id="key4" for="edge" attr.name="name" attr.type="string" />
<key id="key5" for="edge" attr.name="source" attr.type="int" />
<key id="key6" for="node" attr.name="station" attr.type="boolean" />
<key id="key7" for="edge" attr.name="target" attr.type="int" />
<key id="key8" for="node" attr.name="theta" attr.type="double" />
<key id="key9" for="node" attr.name="x" attr.type="double" />
<key id="key10" for="node" attr.name="y" attr.type="double" />
<graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">10000</data>
<data key="key3">node1</data>
<data key="key6">0</data>
<data key="key8">0</data>
<data key="key9">6.95279e-310</data>
<data key="key10">0</data>
</node>
<node id="n1">
<data key="key0">10001</data>
<data key="key3">node1</data>
<data key="key6">0</data>
<data key="key8">0</data>
<data key="key9">6.95279e-310</data>
<data key="key10">0</data>
</node>
<edge id="e0" source="n0" target="n1">
<data key="key1">6.95279e-310</data>
<data key="key2">150</data>
<data key="key4"></data>
<data key="key5">-127787376</data>
<data key="key7">21994</data>
</edge>
</graph>
</graphml>
Mmm. That took a while. But now I see. I know that write_graphviz_dp assumes node_id is the node id property, but as you can see, I tried and it didn't help
Docs To The Rescue
But, wait, docs show a second overload that also takes VertexIndexMap. Let's.... try that?
boost::write_graphml(std::cout, g, vindex, dp);
Now it prints
Live On Coliru
<?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="node" attr.name="id" attr.type="int" />
<key id="key1" for="edge" attr.name="length" attr.type="double" />
<key id="key2" for="edge" attr.name="max_speed" attr.type="double" />
<key id="key3" for="node" attr.name="name" attr.type="string" />
<key id="key4" for="edge" attr.name="name" attr.type="string" />
<key id="key5" for="edge" attr.name="source" attr.type="int" />
<key id="key6" for="node" attr.name="station" attr.type="boolean" />
<key id="key7" for="edge" attr.name="target" attr.type="int" />
<key id="key8" for="node" attr.name="theta" attr.type="double" />
<key id="key9" for="node" attr.name="x" attr.type="double" />
<key id="key10" for="node" attr.name="y" attr.type="double" />
<graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n10000">
<data key="key0">10000</data>
<data key="key3">node1</data>
<data key="key6">0</data>
<data key="key8">0</data>
<data key="key9">6.95279e-310</data>
<data key="key10">0</data>
</node>
<node id="n10001">
<data key="key0">10001</data>
<data key="key3">node1</data>
<data key="key6">0</data>
<data key="key8">0</data>
<data key="key9">6.95279e-310</data>
<data key="key10">0</data>
</node>
<edge id="e0" source="n10000" target="n10001">
<data key="key1">6.95279e-310</data>
<data key="key2">150</data>
<data key="key4"></data>
<data key="key5">-127787376</data>
<data key="key7">21994</data>
</edge>
</graph>
</graphml>
That's likely as close as you'll get with it. Changing the id type to std::string doesn't prevent the "n" prefix. (I suppose it's to allow edges to have id's colliding with nodes without problems?)

Not able to read graphML using boost::read_graphml

I am using boost version 1.64 and trying to read a graphml file using boost::read_graphml..
The code compiles but throws an exception when run:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl >'
what(): parse error: unrecognized type "" for key
Abort (core dumped)
test.cpp
#include <boost/graph/graph_utility.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/dynamic_property_map.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <fstream>
struct GraphData {
std::string Name;
};
struct VertexProperty
{
std::string Name ;
};
struct EdgeProperty
{
std::string Name ;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::directedS, GraphData>;
Graph ReadGraph(std::string const& fileName) {
Graph graph;
boost::dynamic_properties dp;
dp.property("Name", boost::get(&GraphData::Name, graph));
std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
return graph;
}
int main() {
Graph g = ReadGraph("111.graphml");
print_graph(g, get(&GraphData::Name, g));
}
111.graphml
<?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="node" attr.name="Name" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">A</data>
</node>
<node id="n1">
<data key="key0">D</data>
</node>
<node id="n2">
<data key="key0">B</data>
</node>
<node id="n3">
<data key="key0">C</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>
Appreciate any help on this !!!
I can't repro that using the code/xml shown:
GCC 7.3 https://wandbox.org/permlink/HIWgRScfrA0su9dS
Clang 6 https://wandbox.org/permlink/DqkHS3UshUKW4Rsq
GCC 5.1 https://wandbox.org/permlink/vxtxvLYzA97KbBS6
However I do note that you're using GraphData in the position that corresponds tot the Vertex properties bundle.
Perhaps you wanted
using Graph = boost::adjacency_list<boost::setS, boost::vecS,
boost::directedS, VertexProperty, EdgeProperty, GraphData>;
And then use boost::get(&VertexProperty::Name, graph) instead of the GraphData property.
Even more conjecturing, you might want to instruct the graphml parser to ignore any extaneous properties not mapped:
boost::dynamic_properties dp(boost::ignore_other_properties);
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/dynamic_property_map.hpp>
#include <boost/property_map/property_map.hpp>
#include <fstream>
struct GraphData { std::string Name; };
struct VertexProperty { std::string Name; };
struct EdgeProperty { std::string Name; };
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::directedS, VertexProperty, EdgeProperty, GraphData>;
Graph ReadGraph(std::istream& is) {
Graph graph;
boost::dynamic_properties dp(boost::ignore_other_properties);
dp.property("Name", boost::get(&VertexProperty::Name, graph));
boost::read_graphml(is, graph, dp);
return graph;
}
extern std::string const graphml_111;
int main() {
std::istringstream is(graphml_111);
Graph g = ReadGraph(is);
print_graph(g, get(&VertexProperty::Name, g));
}
std::string const graphml_111 = R"(<?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="node" attr.name="Name" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">A</data>
</node>
<node id="n1">
<data key="key0">D</data>
</node>
<node id="n2">
<data key="key0">B</data>
</node>
<node id="n3">
<data key="key0">C</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>)";
Prints
A --> D
D -->
B --> C
C -->

Boost Read_graphml doesn't read xml properly it gives all the vertices but they are empty

I have a adjacency list of type
boost::adjacency_list<boost::setS, boost::vecS, boost::directedS, GraphData>
Where GraphData is a structure contains name
struct GraphItem
{
std::string Name;
}
I am able to write graph to xml
void WriteGraph() {
boost::dynamic_properties dp;
dp.property("Name", make_transform_value_property_map(&Name,
boost::get(vertex_bundle, graph)));
boost::write_graphml(filename, graph, dp, true);
}
std::string Name(boost::vertex_bundle_type<Graph>::type v) {
std::ostringstream oss;
oss << v.Name;
return oss.str();
}
I get XML as
<graphml>
<key id="key0" for="node" attr.name="Name" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical"
parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">A</data>
</node>
<node id="n1">
<data key="key0">D</data>
</node>
<node id="n2">
<data key="key0">B</data>
</node>
<node id="n3">
<data key="key0">C</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>
When I read graph
void ReadGraph() {
boost::dynamic_properties dp;
std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
}
This is crashing says property Name not found.
If I use ignore_other_properties for property,
boost::dynamic_properties dp(ignore_other_properties);
It works but I am not getting any graph item in graph vertices.
The graph is not empty, you get:
0 --> 1
1 -->
2 --> 3
3 -->
Or in XML: https://paste.ubuntu.com/p/c4tGmxGssJ/
Of course, you wanted to read the name property. For that you obviously need to register the property with the dynamic-properties map.
Note You can access members of property bundles much simpler:
dp.property("Name", boost::get(&GraphData::Name, graph));
Full Demo
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
struct GraphData {
std::string Name;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::directedS, GraphData>;
Graph ReadGraph(std::string const& fileName) {
Graph graph;
boost::dynamic_properties dp;
dp.property("Name", boost::get(&GraphData::Name, graph));
std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
return graph;
}
void WriteGraph(std::ostream& os, Graph& graph) {
boost::dynamic_properties dp;
dp.property("Name", get(&GraphData::Name, graph));
boost::write_graphml(os, graph, dp, true);
}
#include <boost/graph/graph_utility.hpp>
int main() {
Graph g = ReadGraph("input.txt");
print_graph(g, get(&GraphData::Name, g));
// or as XML
WriteGraph(std::cout << "==== XML version: ====\n\n", g);
}
Prints
A --> D
D -->
B --> C
C -->
==== XML version: ====
<?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="node" attr.name="Name" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">A</data>
</node>
<node id="n1">
<data key="key0">D</data>
</node>
<node id="n2">
<data key="key0">B</data>
</node>
<node id="n3">
<data key="key0">C</data>
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n2" target="n3">
</edge>
</graph>
</graphml>

Parsing nested xml with boost

I have an xml which I like to read into my program, I've seen a lot of exmaples how to read xml using property tree from boost, however I couldn't make it work for the nested xml that I have:
<?xml version="1.0" encoding="UTF-8"?>
<document version="1.3.0">
<chunk>
<sensors>
<sensor id="0" label="unknown" type="frame">
<resolution width="2056" height="2464"/>
<property name="fixed" value="0"/>
<calibration type="frame" class="adjusted">
<resolution width="2056" height="2464"/>
<fx>7349.85579147491</fx>
<fy>7349.85579147491</fy>
<cx>1028</cx>
<cy>1232</cy>
<p1>0.000308132854297239</p1>
<p2>-0.000521332855614243</p2>
</calibration>
</sensor>
</sensors>
<cameras>
<camera id="0" label="img0000.png" sensor_id="0" enabled="1">
<transform>1.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 -1.0000000000000000e+000 -1.2246467991473532e-016 0.0000000000000000e+000 0.0000000000000000e+000 1.2246467991473532e-016 -1.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 1.0000000000000000e+000</transform>
</camera>
<camera id="1" label="img0011.png" sensor_id="0" enabled="1">
<transform>9.8183676675341047e-001 -2.7892274662900951e-002 -1.8766615162393202e-001 1.3502780959894856e+000 -2.8076662610258110e-002 -9.9960436765543659e-001 1.6760611099915072e-003 -8.8020303958543274e-003 -1.8763865398120155e-001 3.6234208013954891e-003 -9.8223144235654503e-001 -1.1015316085201440e-001 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 1.0000000000000000e+000</transform>
</camera>
</cameras>
<reference>LOCAL_CS["Local Coordinates (m)",LOCAL_DATUM["Local Datum",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]</reference>
<region>
<center>-5.1216167685514069e-002 -7.0600760442627708e-001 -6.9904047240845895e+000</center>
<size>2.1074647950629393e+000 1.5533586459855240e+000 1.0253878730038792e+000</size>
<R>-9.6011547075389880e-001 2.7340714563038887e-001 5.8539008680217816e-002 -2.6221584379471408e-001 -9.5313222127937347e-001 1.5093647677772853e-001 9.7062526662174770e-002 1.2956659089939432e-001 9.8680867671533157e-001</R>
</region>
<settings>
<property name="accuracy_tiepoints" value="1"/>
<property name="accuracy_cameras" value="10"/>
<property name="accuracy_cameras_ypr" value="2"/>
<property name="accuracy_markers" value="0.005"/>
<property name="accuracy_scalebars" value="0.001"/>
<property name="accuracy_projections" value="0.1"/>
</settings>
</chunk>
</document>
I'm only interested in reading the cameras node and their attributes, and I've used the following code for that, but it doesn't work:
using namespace std;
using namespace boost;
using namespace boost::property_tree;
const ptree& empty_ptree() {
static ptree t;
return t;
}
int main()
{
ptree tree;
read_xml(XML_PATH1, tree);
tree = tree.get_child("document.chunk", empty_ptree());
const ptree & formats = tree.get_child("cameras.camera", empty_ptree());
BOOST_FOREACH(const ptree::value_type & f, formats)
{
string at = f.first + ".<xmlattr>";
const ptree & attributes = f.second.get_child("<xmlattr>", empty_ptree());
cout << "Extracting attributes from " << at << ":" << endl;
BOOST_FOREACH(const ptree::value_type &v, attributes)
{
cout << "First: " << v.first.data() << " Second: " << v.second.data() << endl;
}
}
}
so, for each camera you want to iterate over each attribute, don't you ?
if yes, then you need to use equal_range() instead of get_child(); the latter returns a single child, the former a range of associative iterators pointing to all childs with the given key.
so, use get_child() to get document.chunk.cameras, use equal_range('camera') to get all cameras and for each camera use equal_range('xmlattr') to traverse its attributes.

Is it possible to generate non-blocking calls with dbusxx-xml2cpp?

I would like to generate C++ headers using dbusxx-xml2cpp where some methods are non-blocking, e.g. using invoke_method_noreply instead of invoke_method. Is this possible?
For example the following XML:
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/me/MyService">
<interface name="me.MyService">
<method name="MyMethod">
<arg direction="in" type="s" name="argument"/>
</method>
</interface>
</node>
Would generate (partly):
void MyMethod(const std::string& argument)
{
::DBus::CallMessage call;
::DBus::MessageIter wi = call.writer();
wi << argument;
call.member("MyMethod");
::DBus::Message ret = invoke_method (call);
}
But I would like to have something like:
void MyMethod(const std::string& argument)
{
::DBus::CallMessage call;
::DBus::MessageIter wi = call.writer();
wi << argument;
call.member("MyMethod");
bool ret = invoke_method_noreply (call);
}
Use the annotation org.freedesktop.DBus.Method.NoReply"
Example XML:
<node>
<interface name="org.test.example">
<method name="NoReplyMethod">
<arg name="data" direction="in" type="i"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
</interface>
</node>
Generated Code:
void NoReplyMethod(const int32_t& data)
{
::DBus::CallMessage call;
::DBus::MessageIter wi = call.writer();
wi << data;
call.member("NoReplyMethod");
assert (invoke_method_noreply (call));
}