简体   繁体   中英

How to interpret arbitrary strings as graph properties when reading a graphML file using `boost::read_graphml`?

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);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM