あめだまふぁくとりー

Boost.Graphとかできますん

Propety_iter_range

BGLといえばPropertyMap,PropertyMapといえばBGLなのだが,BGLのアダプタ関数はイテレータのペアであるRangeを返すなのでBGLとPropertyMap,Rangeをうまく組み合わせて使いたい.
で少し調べてみたら,BGL内にproperty_iter_rangeというのを発見.property_iteratorイテレータが指し示す値に,その値をキーとするプロパティマップに適用するイテレータアダプタ.このペアを作るのがproperty_iter_range.
以下サンプル

#include <iostream>
#include <random>
#include <ctime>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/plod_generator.hpp>
#include <boost/graph/property_iter_range.hpp>  // property_iter_range
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/generate.hpp>
#include <boost/range/numeric.hpp>

int main()
{
	typedef boost::adjacency_list<
				boost::vecS, boost::vecS, boost::undirectedS,
				boost::property<boost::vertex_root_t, bool,
					boost::property<boost::vertex_distance_t, int>>,
				boost::property<boost::edge_weight_t, int>>
			Graph;

	typedef boost::plod_iterator<std::mt19937, Graph> PlodGen;

	std::size_t const num_v = 20;
	std::mt19937 gen{
		static_cast<std::mt19937::result_type>(time(nullptr))
	};
	// ランダムグラフの生成
	Graph graph{
		PlodGen{gen, num_v, 1.5, 200},
		PlodGen{},
		num_v
	};

	std::uniform_int_distribution<> bool_dist{0, 1};
	boost::generate(
			boost::get_property_iter_range(graph, boost::vertex_root),
			std::bind(bool_dist, gen));

	std::uniform_int_distribution<> dist{0, 15};
	boost::generate(
			boost::get_property_iter_range(graph, boost::edge_weight),
			std::bind(dist, gen));

	boost::dijkstra_shortest_paths(
			graph,
			vertex(std::uniform_int_distribution<>{0, num_v - 1}(gen), graph),
			boost::weight_map(get(boost::edge_weight, graph)).
			distance_map(get(boost::vertex_distance, graph)));

        // rootから各vertexまでの距離の合計の計算
	auto const is_root_func
		= boost::make_property_map_function(get(boost::vertex_root, graph));
	auto const vrange
		= vertices(graph) | boost::adaptors::filtered(is_root_func);
	int total = 0;
	for (auto const v : vrange) {
		total += get(boost::vertex_distance, graph, v);
	}
	std::cout << total << std::endl;

	return 0;
}

正直これはむむむ...propertyp_iteratorのレンジを得るにはget_property_iter_rangeという関数にグラフオブジェクトとPropertyTagのオブジェクトを渡す必要がある.property_iter_range.hppのファイルがPropertyMapではなくBGL内にあるからグラフに特化したインタフェースなのかもしれないが,これだと使いにくい.
例えば上のサンプルの最後の距離の合計の計算の場合,フィルタを掛けたvrangeにプロパティマップでマッピングできた方がいい.絶対にいい!
というわけでRangeで一般的なAdaptorを書いてみた.
https://github.com/amedama41/Canard/blob/master/property_map/property_map_adaptor.hpp
ついてでにBoostのproperty_iteratorはWritable PropertyMapには適用できなかったので,Writable PropertyMapにも対応させてみた.
これを使えばサンプルの計算も

boost::accumulate(
		vrange | Canard::adaptors::mapped(get(boost::vertex_distance, graph)),
		0.0);

みたいに書けていい感じなのではないだろうか