回应您的
最后的想法:需要注意的是,boost 精神和 BGL 领域的学习曲线都相当陡峭。基于此,如果分开,可读性和实现会更容易。不是说不能解决。
在你的回答中,我同意。保持简单的关键是分离关注点。 (Phoenix 允许您将几乎所有内容直接绑定到解析器中,但这并不意味着这是一个好主意[1]。)
也许这个示例应用程序可以作为灵感?
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <sstream>
using namespace boost;
typedef adjacency_list<setS, vecS, directedS> Graph;
std::vector<std::string> generate_file_data();
void merge_into(std::istream&& is, Graph& into);
int main() {
Graph merged;
for(auto& input: generate_file_data())
merge_into(std::istringstream(input), merged);
print_graph(merged);
}
如您所见,它读取许多(不同的)文件并将边合并到merged 图中。
当然,在一个在线演示中,我们没有输入文件,所以我在 20 个顶点 (generate_file_data()) 中生成了 5 个随机图,其中包含 6 条随机边。
有趣的是merge_into:
#include <boost/spirit/include/qi.hpp>
void merge_into(std::istream&& is, Graph& into) {
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
phx::function<edge_merge_f> merge_edges;
boost::spirit::istream_iterator f(is >> std::noskipws), l;
bool ok = qi::phrase_parse(f, l,
(qi::int_ >> *qi::int_) [ merge_edges(qi::_1, qi::_2, phx::ref(into)) ] % qi::eol,
qi::blank);
assert(ok);
}
这是处理精神的唯一一段代码。如您所见,它实际上根本不了解 BGL。该逻辑留给“回调”函数对象edge_merge_f,这也很简单:
struct edge_merge_f {
template <typename...> struct result { typedef void type; }; // for BOOST_RESULT_OF_USE_TR1:
template<typename V, typename Edges, typename G>
void operator()(V const& v, Edges const& edges, G& into) const {
for (auto e : edges)
if (!add_edge(v, e, into).second)
std::cout << "Duplicate edge: (" << v << "," << e << ")\n";
}
};
更新作为奖励,一个版本通过将EdgeCallback 作为boost::function 传递给它,将merge_adjacencies 与Graph 类型完全分离:Decoupled version强>。当然,这会降低效率,但它表明了我将事物分开的意思。
所以你有它。这是我机器上的输出(我没有为随机引擎播种,因此它是可重复的):Live On Coliru
Duplicate edge: (9,1)
Duplicate edge: (0,4)
0 --> 1 2 4 9
1 -->
2 -->
3 --> 1 7
4 --> 1
5 --> 1 6
6 --> 1 3 5
7 --> 5 6 8
8 --> 3
9 --> 1 2
如果将 Graph typedef 更改为使用 vecS 作为边缘容器,则无需更改任何其他内容,结果改为:Live On Coliru
0 --> 4 9 1 4 2
1 -->
2 -->
3 --> 7 1
4 --> 1
5 --> 1 6
6 --> 1 3 5
7 --> 5 6 8
8 --> 3
9 --> 1 2 1
[1] 我想我之前把你链接到Boost Spirit: "Semantic actions are evil"? :)
完整列表
为了后代(也包括generate_file_data()):
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <sstream>
using namespace boost;
typedef adjacency_list<setS/*vecS*/, vecS, directedS> Graph;
std::vector<std::string> generate_file_data();
void merge_into(std::istream&& is, Graph& into);
int main() {
Graph merged;
for(auto& input: generate_file_data())
merge_into(std::istringstream(input), merged);
print_graph(merged);
}
////////////////////////////////
// Generating random test data
#include <boost/graph/random.hpp>
#include <boost/random.hpp>
std::vector<std::string> generate_file_data() {
std::vector<std::string> data;
mt19937 prng(42);
for (int i=0; i<5; ++i) {
std::ostringstream oss;
Graph g;
generate_random_graph(g, 10, 4, prng);
for (auto v : make_iterator_range(vertices(g))) {
oss << v << " ";
for (auto const& e : make_iterator_range(out_edges(v, g))) oss << target(e, g) << " ";
oss << "\n";
}
data.push_back(oss.str());
}
return data;
}
////////////////////////////////
// Merging edges
namespace {
struct edge_merge_f {
template <typename...> struct result { typedef void type; }; // for BOOST_RESULT_OF_USE_TR1:
template<typename V, typename Edges, typename G>
void operator()(V const& v, Edges const& edges, G& into) const {
for (auto e : edges)
if (!add_edge(v, e, into).second)
std::cout << "Duplicate edge: (" << v << "," << e << ")\n";
}
};
}
#include <boost/spirit/include/qi.hpp>
void merge_into(std::istream&& is, Graph& into) {
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
phx::function<edge_merge_f> merge_edges;
boost::spirit::istream_iterator f(is >> std::noskipws), l;
bool ok = qi::phrase_parse(f, l,
(qi::int_ >> *qi::int_) [ merge_edges(qi::_1, qi::_2, phx::ref(into)) ] % qi::eol,
qi::blank);
assert(ok);
}