Boost.Graphのadjacency_list
クラスには、Bundleプロパティという仕組みを使って、頂点や辺の情報を、任意のユーザー定義型に保持できます。
そのような作りになっているグラフオブジェクトに、Graphvizのデータを読み込むサンプルコードを、以下の示します。
読み込むGraphvizデータ(graph.dot)
digraph G { 0 [name="A", pos="(0,0)", id=1]; 1 [name="B", pos="(-3,3)"]; 2 [name="C", pos="(3,3)"]; 3 [name="D", pos="(0,6)"]; 0->1; 0->2; 1->3; 2->3; }
このデータを読み込むコード:
#define BOOST_GRAPH_USE_SPIRIT_PARSER // for header only #include <boost/graph/adjacency_list.hpp> #include <boost/graph/graphviz.hpp> #include <boost/optional.hpp> #include <fstream> #include <iostream> struct Point { int x = 0; int y = 0; }; std::ostream& operator<<(std::ostream& os, const Point& p) { return os << '(' << p.x << ',' << p.y << ')'; } std::istream& operator>>(std::istream& is, Point& p) { if (is.get() != '(') { is.setstate(std::ios_base::failbit); return is; } is >> p.x; if (!is) { is.setstate(std::ios_base::failbit); return is; } if (is.get() != ',') { is.setstate(std::ios_base::failbit); return is; } is >> p.y; if (!is) { is.setstate(std::ios_base::failbit); return is; } if (is.get() != ')') { is.setstate(std::ios_base::failbit); } return is; } namespace boost { template <class T> std::ostream& operator<<(std::ostream& os, const boost::optional<T>& x) { if (x) { os << x.get(); } else { os << ""; } return os; } template <class T> std::istream& operator>>(std::istream& is, boost::optional<T>& x) { T result; if (is >> result) { x = result; } return is; } } struct Vertex { int nodeId; std::string name; boost::optional<int> id; Point pos; }; using Graph = boost::adjacency_list< boost::listS, boost::vecS, boost::directedS, Vertex >; void load(Graph& g) { boost::dynamic_properties dp; dp.property("node_id", boost::get(&Vertex::nodeId, g)); dp.property("name", boost::get(&Vertex::name, g)); dp.property("id", boost::get(&Vertex::id, g)); dp.property("pos", boost::get(&Vertex::pos, g)); std::ifstream file("graph.dot"); if (!boost::read_graphviz(file, g, dp)) { throw std::runtime_error("can't load graph.dot file."); } } int main() { Graph g; load(g); std::cout << "name : " << g[0].name << std::endl; std::cout << "pos : " << g[0].pos << std::endl; }
出力:
name : A pos : (0,0)
Bundleプロパティに設定されたユーザー定義型と、Graphvizの情報をマッピングするには、boost::dynamic_properties
クラスを使用します。ここに、属性名と、それに相当するBundleプロパティのメンバ変数を列挙していきます。
ここでは、Graphvizの頂点データに含まれる、以下の情報を読み込んでいます:
- ノードID (頂点ID)
- これは、0, 1, 2, 3といった頂点のIDにあたります。
"node_id"
という名前で、このIDを読み込めます。このIDがいらない場合は、読み込まなくてもいいです。
- これは、0, 1, 2, 3といった頂点のIDにあたります。
- 頂点の名前
name
という名前に設定された属性は、std::string
型のメンバ変数に読み込んでいます。
- ID
- 頂点に、ノードID以外の任意のIDを持たせています。これは省略可能な要素とし、
boost::optional<int>
型に読み込んでいます。そのための入出力ストリーム演算子を用意してあります。
- 頂点に、ノードID以外の任意のIDを持たせています。これは省略可能な要素とし、
- 頂点の表示位置
- 表示位置は、ユーザー定義型の
Point
型に読み込んでいます。これは、(x,y)
という形式で書いています。
- 表示位置は、ユーザー定義型の
頂点や辺の各属性を読み込む際には、その型の入力ストリーム演算子が呼ばれます。ここで、任意のフォーマットになっているユーザー定義型のデータを読み込めます。
boost::optional
の読み込みについては、公式の入出力ストリームとはフォーマットが異なることに注意です。ここでは、その属性を読み込めなかった場合に無効な状態、読み込めた場合に有効な状態としています。boost::optional
公式のフォーマットを使用する場合には、<boost/optional/optional_io.hpp>
をインクルードしてください。
optional
に関連して。属性が記載されていない場合には、operator>>
は呼ばれず、その型のデフォルト値が設定されます。