簡體   English   中英

如何一般訪問 Boost Graph 捆綁屬性?

[英]How do I Generically Access the Boost Graph Bundled Properties?

我正在編寫一個 c++ 模板來讀取GraphVizGraphML格式的加權圖。

困難在於我使用具有不同頂點/邊束的不同圖形類型,它們可能看起來像

struct EdgeBundle_1 {
    double weight = 1.;
};

struct EdgeBundle_2 {
    double weight = 2.;
    int some_int;
};

using Graph_1 = typename boost::adjacency_list<boost::listS,
                                               boost::vecS,
                                               boost::undirectedS,
                                               VertexBundle,
                                               EdgeBundle_1>;

using Graph_2 = typename boost::adjacency_list<boost::listS,
                                               boost::vecS,
                                               boost::undirectedS,
                                               VertexBundle,
                                               EdgeBundle_2>;

現在我想訪問任意Graph的邊緣包,所以我必須在以下僅適用於Graph_1的代碼中替換&EdgeBundle_1 EdgeBundle_1

template <typename Graph>
void do_sth_with_bundled_weight(Graph& g){
    boost::dynamic_properties dp(boost::ignore_other_properties);
    dp.property("weight", boost::get(&EdgeBundle_1::weight, g));

    ... // here, I read in the graph via `boost::read_graphml(if_stream, g, dp);`
}

此 boost 文檔頁面的最底部,除了如何訪問捆綁屬性的類型之外,我找不到任何其他內容。

我很感激任何幫助::)

對於 BGL,真正的問題是“如何做任何非通用的事情”:)

所以,你可以像 boost 那樣做。 所有算法都采用屬性映射,抽象出圖形元素及其屬性之間的關系。

通常這將與特定於算法的臨時屬性有關,但沒有什么可以阻止您在更多地方使用它。

最好的事情是,您已經擁有屬性 map,而它正是變量部分: get(&EdgeBundle_1::weight, g) ,因此只需將其作為參數:

template <typename Graph, typename WeightMap>
void write_graph(std::ostream& os, Graph& g, WeightMap weight_map) {
    boost::dynamic_properties dp;
    dp.property("weight", weight_map);

    boost::write_graphml(os, g, dp);
}

template <typename Graph, typename WeightMap>
void read_graph(Graph& g, WeightMap weight_map) {
    boost::dynamic_properties dp(boost::ignore_other_properties);
    dp.property("weight", weight_map);

    std::ifstream ifs("input.xml", std::ios::binary);
    g.clear();
    boost::read_graphml(ifs, g, dp);
}

您甚至可以將其設為庫默認邊權重 map:

template <typename Graph> void read_graph(Graph& g) {
    return read_graph(g, get(boost::edge_weight, g));
}

template <typename Graph> void write_graph(std::ostream& os, Graph& g) {
    return write_graph(os, g, get(boost::edge_weight, g));
}

演示:讀取和比較相等

往返以下 XML Graph_1 和 Graph_2 並檢查相等性:

<?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="edge" attr.name="weight" attr.type="double" />
  <graph id="G" edgedefault="undirected" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
    <node id="n0">
    </node>
    <node id="n1">
    </node>
    <node id="n2">
    </node>
    <node id="n3">
    </node>
    <node id="n4">
    </node>
    <node id="n5">
    </node>
    <node id="n6">
    </node>
    <node id="n7">
    </node>
    <node id="n8">
    </node>
    <node id="n9">
    </node>
    <edge id="e0" source="n0" target="n7">
      <data key="key0">2.2</data>
    </edge>
    <edge id="e1" source="n7" target="n3">
      <data key="key0">3.3</data>
    </edge>
    <edge id="e2" source="n3" target="n2">
      <data key="key0">4.4</data>
    </edge>
  </graph>
</graphml>

在此處輸入圖像描述

住在科利魯

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <iostream>
#include <fstream>

struct VertexBundle {};

struct EdgeBundle_1 {
    double weight = 1.;
};

struct EdgeBundle_2 {
    double weight = 2.;
    int some_int;
};

using Graph_1 = typename boost::adjacency_list<
    boost::listS, boost::vecS, boost::undirectedS, VertexBundle, EdgeBundle_1>;

using Graph_2 = typename boost::adjacency_list<
    boost::listS, boost::vecS, boost::undirectedS, VertexBundle, EdgeBundle_2>;

template <typename Graph, typename WeightMap>
void write_graph(std::ostream& os, Graph& g, WeightMap weight_map) {
    boost::dynamic_properties dp;
    dp.property("weight", weight_map);

    boost::write_graphml(os, g, dp);
}

template <typename Graph, typename WeightMap>
void read_graph(std::istream& is, Graph& g, WeightMap weight_map) {
    boost::dynamic_properties dp(boost::ignore_other_properties);
    dp.property("weight", weight_map);

    g.clear();
    boost::read_graphml(is, g, dp);
}

template <typename Graph> void read_graph(std::istream& is, Graph& g) {
    return read_graph(is, g, get(boost::edge_weight, g));
}

template <typename Graph> void write_graph(std::ostream& os, Graph& g) {
    return write_graph(os, g, get(boost::edge_weight, g));
}

extern std::string const demo_xml;

int main() {
    Graph_1 g1;
    Graph_2 g2;
    auto w1 = get(&EdgeBundle_1::weight, g1);
    auto w2 = get(&EdgeBundle_2::weight, g2);

    auto roundtrip = [](auto g, auto w) {
        {
            std::istringstream is(demo_xml);
            read_graph(is, g, w);
        }

        std::ostringstream os;
        write_graph(os, g, w);
        return os.str();
    };

    auto xml1 = roundtrip(Graph_1{}, w1);
    auto xml2 = roundtrip(Graph_2{}, w2);

    std::cout << "Equal:" << std::boolalpha << (xml1 == xml2) << "\n";
}

印刷

Equal:true

獎金

要獲得更多自動化,您可以告訴 BGL 您的帶有特征的屬性映射,這樣您就不必再手動指定它了。

更新將此添加為手指練習。 警告:這不適合膽小的人。 就代碼行而言,我看起來很無害,但實際上它非常密集,並且使用了許多高級和微妙的庫和語言特性。

在自定義命名空間(例如MyLib )中,我們創建了一個可以自定義的包裝器類型:

template <typename Impl> struct Graph {
    Impl&       graph() { return _impl; };
    Impl const& graph() const { return _impl; };
    void clear() { _impl.clear(); }
  private:
    Impl _impl;
};

接下來,我們委托常見的 BGL 操作:

namespace detail {
    template <typename... T> static auto& fwd_impl(Graph<T...>& g) {
        return g.graph();
    }
    template <typename... T> static auto const& fwd_impl(Graph<T...> const& g) {
        return g.graph();
    }
    template <typename T> static decltype(auto) fwd_impl(T&& v) {
        return std::forward<T>(v);
    }
}

#define DELEGATE_ONE(r, _, name)                                               \
    template <typename... Args>                                                \
    static inline decltype(auto) name(Args&&... args) {                        \
        return (boost::name)(                                                  \
            detail::fwd_impl(std::forward<decltype(args)>(args))...);          \
    }
#define DELEGATE(...)                                                          \
    BOOST_PP_SEQ_FOR_EACH(DELEGATE_ONE, _,                                     \
                          BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

    DELEGATE(add_vertex, add_edge, vertices, edges, num_vertices, num_edges,
             out_edges, out_degree, get, source, target, vertex)
#undef DELEGATE
#undef DELEGATE_ONE

是的。 好多啊。 基本上,如果 ADL 關聯我們的命名空間,我們會委托所有命名的自由函數,並且我們將所有 arguments 轉發到未修改的 boost 版本,除了Graph<>包裝器被它的_impl替換。

接下來,我們添加我們正在尋找的扭曲:

// The crux: overriding the edge_weight map to access the bundle
template <typename Impl> auto get(boost::edge_weight_t, Graph<Impl>& g) {
    auto bundle_map = boost::get(boost::edge_bundle, g.graph());
    auto accessor   = [](auto& bundle) -> decltype(auto) {
        return access_edge_weight(bundle);
    };

    return boost::make_transform_value_property_map(accessor, bundle_map);
}

我們使用包裝器重新定義原始圖:

using Graph_1 = Graph<GraphImpl_1>;
using Graph_2 = Graph<GraphImpl_2>;

性狀

特征不是自由函數,需要在我們的命名空間之外。 為簡潔起見,我使用 c++17 語法:

template <typename Impl>
struct boost::graph_traits<MyLib::Graph<Impl>> : boost::graph_traits<Impl> {};

template <typename Impl, typename Property>
struct boost::graph_property<MyLib::Graph<Impl>, Property>
    : boost::graph_property<Impl, Property> {};

template <typename Impl, typename Property>
struct boost::property_map<MyLib::Graph<Impl>, Property>
    : boost::property_map<Impl, Property> {};

那里。 這告訴 BGL 我們的圖是我們的特征/屬性映射的實現類型。

但是,我們確實覆蓋edge_weight_t map:

template <typename Impl>
struct boost::property_map<MyLib::Graph<Impl>, boost::edge_weight_t> {
    using Wrapper = MyLib::Graph<Impl>;
    using type = decltype(MyLib::get(boost::edge_weight, std::declval<Wrapper&>()));
    using const_type = decltype(MyLib::get(boost::edge_weight, std::declval<Wrapper const&>()));
};

(為簡潔起見,再次大量使用 c++14 功能。)

證據

所有這些神奇的東西讓我們現在不必提供屬性 map,而是自動檢測到它:

住在科利魯

int main() {
    auto roundtrip = [](auto g) {
        std::istringstream is(demo_xml);
        read_graph(is, g);

        std::ostringstream os;
        write_graph(os, g);
        return os.str();
    };

    std::cerr << "Equal:" << std::boolalpha
              << (roundtrip(MyLib::Graph_1{}) == roundtrip(MyLib::Graph_2{}))
              << "\n";
}

仍然打印

Equal:true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM