I have a graph type where each Vertex carries a std::vector<int>
as a property.
struct VertexProperties {
std::vector<int> numbers;
};
using Graph = boost::adjacency_list<
boost::vecS, boost::vecS, boost::undirectedS, VertexProperties>;
I wrote an example object of my graph type to a GraphML file using boost::write_graphml
. To do so, I used boost::make_transform_value_property_map
to convert the std::vector<int>
property to a std::string
. The GraphML file has the following contents:
<?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="numbers" attr.type="string" />
<graph id="G" edgedefault="undirected" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">1 2 3 </data>
</node>
</graph>
</graphml>
Now I would like to read the file back in to reobtain the graph (in a different program) using boost::read_graphml
. To do so, it is necessary to create a boost::dynamic_properties
object and add to that a property map that can understand the information found in the GraphML file and set the correct vertex property accordingly.
How can the latter property map be defined?
I solved my problem by writing a custom property map class template TranslateStringPMap
that wraps an existing property map and takes two function objects that convert between strings and the wrapped map's value type.
File translate_string_pmap.hpp
:
#ifndef TRANSLATE_STRING_PMAP_H
#define TRANSLATE_STRING_PMAP_H
#include <string>
#include <boost/property_map/property_map.hpp>
template <typename PMap, typename ToString, typename FromString>
class TranslateStringPMap {
public:
using category = boost::read_write_property_map_tag;
using key_type = typename boost::property_traits<PMap>::key_type;
using reference = std::string;
using value_type = std::string;
TranslateStringPMap(
PMap wrapped_pmap, ToString to_string, FromString from_string)
: wrapped_pmap{wrapped_pmap},
to_string{to_string},
from_string{from_string} {}
auto friend get(TranslateStringPMap const& translator, key_type const& key)
-> value_type {
return translator.to_string(get(translator.wrapped_pmap, key));
}
auto friend put(
TranslateStringPMap const& translator, key_type const& key,
value_type const& value) -> void {
boost::put(translator.wrapped_pmap, key, translator.from_string(value));
}
private:
PMap wrapped_pmap;
ToString to_string;
FromString from_string;
};
#endif
By customizing the conversion function objects to_string
and from_string
, TranslateStringPMap
can be added to a boost::dynamic_properties
object to facilitate reading and writing of arbitrary graph property types. The following file gives a usage example.
File graph_rw.cpp
:
#include <sstream>
#include <string>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/dynamic_property_map.hpp>
#include "translate_string_pmap.hpp"
struct VertexProperties {
std::vector<int> numbers;
};
using Graph = boost::adjacency_list<
boost::vecS, boost::vecS, boost::undirectedS, VertexProperties>;
auto vec2str(std::vector<int> const& vec) -> std::string {
auto str = std::string{};
for (auto number : vec) {
str += std::to_string(number) += " ";
}
return str;
}
auto str2vec(std::string const& str) -> std::vector<int> {
auto strs = std::stringstream{str};
auto number = 0;
auto vec = std::vector<int>{};
while (strs >> number) {
vec.push_back(number);
}
return vec;
}
auto write_my_graphml(Graph& graph, std::ofstream& output_stream) -> void {
auto dprops = boost::dynamic_properties{};
dprops.property(
"numbers",
TranslateStringPMap{
boost::get(&VertexProperties::numbers, graph), vec2str, str2vec});
boost::write_graphml(output_stream, graph, dprops);
}
auto read_my_graphml(std::ifstream& input_stream) -> Graph {
auto graph = Graph{};
auto dprops = boost::dynamic_properties{};
dprops.property(
"numbers",
TranslateStringPMap{
boost::get(&VertexProperties::numbers, graph), vec2str, str2vec});
boost::read_graphml(input_stream, graph, dprops);
return graph;
}
auto main() -> int {
{
auto graph1 = Graph{};
boost::add_vertex(VertexProperties{{1, 2, 3}}, graph1);
auto out_stream = std::ofstream{"graph1.gml"};
write_my_graphml(graph1, out_stream);
}
{
auto in_stream = std::ifstream{"graph1.gml"};
auto graph2 = read_my_graphml(in_stream);
auto out_stream = std::ofstream{"graph2.gml"};
write_my_graphml(graph2, out_stream);
}
}