【问题标题】:Using Boost, how can I put/get custom edge properties as a struct?使用 Boost,如何将自定义边缘属性作为结构放置/获取?
【发布时间】:2021-07-10 15:01:49
【问题描述】:

我已经阅读了这里的文档 (https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/using_adjacency_list.html) 和几个堆栈溢出页面两个小时,但这里根本没有取得任何进展。我有一个图表,其中边缘既有距离又有坑洞的数量(撞到 3 个坑洞后车辆会中断),所以我想我会使用自定义结构并设置边缘属性。

把它放在类声明的上方:

struct road_details_t {
  typedef boost::edge_property_tag kind;
};
struct road_info {
  unsigned miles;
  bool pothole;
};
typedef boost::property<road_details_t, struct road_info> RoadDetailsProperty;

编辑:我相信这是上述方法的替代方案,也没有运气:

namespace boost {
    enum road_details_t { road_details };
    BOOST_INSTALL_PROPERTY(edge, road_details);
}

类声明中的邻接图:

typedef boost::adjacency_list<boost::listS,       // store edges in lists
                              boost::vecS,        // store vertices in a vector
                              boost::undirectedS
                              RoadDetailsProperty
                              >
    Graph;
typedef boost::graph_traits<Graph> GraphTraits;
typedef GraphTraits::vertex_descriptor Vertex;
typedef GraphTraits::edge_descriptor Edge;

Graph roadMap;

稍后在一个函数中添加道路:

void City::addRoad(Intersection intersection1, Intersection intersection2, unsigned time, bool pothole) 
{
    unsigned nV = num_vertices(roadMap);
    while (intersection1 >= nV)
        nV = add_vertex(roadMap)+1;
    while (intersection2 >= nV)
        nV = add_vertex(roadMap)+1;
    add_edge(intersection1, intersection2, roadMap);

所以现在我正在尝试输入/获取道路信息,其中有多个错误,并且我尝试了很多变体:

// get the edge
std::pair<Edge, bool> edg = edge(intersection1, intersection2, roadMap);

// get the property map for road details. ERROR: no matching function call to get()
typename property_map<Graph, road_details_t>::type
  roadDetailMap = get(road_details_t(), roadMap);

// create struct from arguments passed in
struct road_info info = {time, pothole};

// attempt to add to roadDetailMap
put(roadDetailMap, edg, info);
// roadDetailMap[edg] = info; // also not working

// attempt to fetch information
cout << get(roadDetailMap, edg).miles << endl;
// cout << roadDetailMap[edg].miles << endl; // also not working

通过注释掉不同的行,我发现问题似乎在于我如何获取属性图,但是在网上查看示例时,我觉得我正在做与他们完全相同的事情。

目标最终只是弄清楚如何添加道路信息以及以后如何获取它,因此可以接受完全不同的方法,我这里的只是我尝试过(不成功)但我觉得取得了最有希望的结果。

【问题讨论】:

    标签: c++ boost boost-graph boost-property-map


    【解决方案1】:

    可以按照您的建议进行操作,这需要使用BOOST_INSTALL_PROPERTY 注册属性标签(请参阅example/examples/edge_property.cpp for an example)。相关sn-p:

    namespace boost {
      enum edge_flow_t { edge_flow };
      enum edge_capacity_t { edge_capacity };
    
      BOOST_INSTALL_PROPERTY(edge, flow);
      BOOST_INSTALL_PROPERTY(edge, capacity);
    }
    

    现在您可以在属性定义中使用新的属性标签 就像您使用内置标签之一一样。

      typedef property<capacity_t, int> Cap;
      typedef property<flow_t, int, Cap> EdgeProperty;
      typedef adjacency_list<vecS, vecS, no_property, EdgeProperty> Graph;
    

    但是,由于您的用例如此频繁,因此任何用户定义的结构都已经有一个内置标记:vertex_bundle_tedge_bundle_t

    捆绑属性

    这是一个使用它的示例。文档:https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/bundles.html

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/graph_utility.hpp>
    
    struct road_info {
        unsigned miles;
        bool     pothole;
    };
    
    using Graph  = boost::adjacency_list<boost::listS, boost::vecS,
                                        boost::undirectedS, road_info>;
    using Vertex = Graph::vertex_descriptor;
    using Edge   = Graph::edge_descriptor;
    
    int main() {
        Graph roadMap;
    
        Vertex road1     = add_vertex(road_info{15, false}, roadMap);
        Vertex road2     = add_vertex(road_info{3, true}, roadMap);
        /*Vertex road3 =*/ add_vertex(road_info{27, false}, roadMap);
    
        add_edge(road1, road2, roadMap);
    
        print_graph(roadMap);
    
        auto bmap = get(boost::vertex_bundle, roadMap);
    
        for (Vertex v : boost::make_iterator_range(vertices(roadMap))) {
            road_info& bundle = bmap[v];
            // or even easier
            // road_info& info = roadMap[v];
            auto& [miles, pothole] = roadMap[v]; // c++17
    
            std::cout << "Vertex #" << v << " " << miles << " miles, "
                      << "pothole:" << std::boolalpha << pothole << "\n";
        }
    
        // individual maps
        auto miles_map = get(&road_info::miles, roadMap);
        auto poth_map = get(&road_info::pothole, roadMap);
    
        for (Vertex v : boost::make_iterator_range(vertices(roadMap))) {
            std::cout << "Vertex #" << v << " " << miles_map[v] << " miles, "
                      << "pothole:" << std::boolalpha << poth_map[v] << "\n";
    
            put(poth_map, v, false); // reset pothole
        }
    }
    

    打印

    0 <--> 1 
    1 <--> 0 
    2 <--> 
    Vertex #0 15 miles, pothole:false
    Vertex #1 3 miles, pothole:true
    Vertex #2 27 miles, pothole:false
    Vertex #0 15 miles, pothole:false
    Vertex #1 3 miles, pothole:true
    Vertex #2 27 miles, pothole:false
    

    更新:边缘捆绑

    对于 cmets 来说,把它变成一个边缘束只是表面上的修改:

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/graph_utility.hpp>
    
    struct road_info {
        unsigned miles;
        bool     pothole;
    };
    
    using Graph =
        boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS,
                              boost::no_property, road_info>;
    using Vertex = Graph::vertex_descriptor;
    using Edge   = Graph::edge_descriptor;
    
    int main() {
        Graph roadMap(3);
    
        add_edge(0, 1, road_info{15, false}, roadMap);
        add_edge(1, 2, road_info{3, true}, roadMap);
    
        print_graph(roadMap);
    
        auto bmap = get(boost::edge_bundle, roadMap);
    
        for (Edge e : boost::make_iterator_range(edges(roadMap))) {
            road_info& bundle = bmap[e];
            // or even easier
            // road_info& info = roadMap[e];
            auto& [miles, pothole] = roadMap[e]; // c++17
    
            std::cout << "Edge " << e << " " << miles << " miles, "
                      << "pothole:" << std::boolalpha << pothole << "\n";
        }
    
        // individual maps
        auto miles_map = get(&road_info::miles, roadMap);
        auto poth_map = get(&road_info::pothole, roadMap);
    
        for (Edge e : boost::make_iterator_range(edges(roadMap))) {
            std::cout << "Edge " << e << " " << miles_map[e] << " miles, "
                      << "pothole:" << std::boolalpha << poth_map[e] << "\n";
    
            put(poth_map, e, false); // reset pothole
        }
    }
    

    印刷:

    0 <--> 1 
    1 <--> 0 2 
    2 <--> 1 
    Edge (0,1) 15 miles, pothole:false
    Edge (1,2) 3 miles, pothole:true
    Edge (0,1) 15 miles, pothole:false
    Edge (1,2) 3 miles, pothole:true
    

    对于受虐狂

    如果您坚持,您当然可以安装属性标签。接触个别成员会很不舒服(并且可能效率低下),但你会这样做:

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/graph_utility.hpp>
    #include <boost/property_map/transform_value_property_map.hpp>
    
    struct road_info {
        unsigned miles;
        bool     pothole;
    };
    
    namespace boost {
        struct edge_road_info_t {};
        BOOST_INSTALL_PROPERTY(edge, road_info);
        static inline constexpr edge_road_info_t road_info{};
    }
    
    using Graph =
        boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS,
                              boost::no_property, 
                              boost::property<boost::edge_road_info_t, road_info> >;
    using Vertex = Graph::vertex_descriptor;
    using Edge   = Graph::edge_descriptor;
    
    int main() {
        Graph roadMap(3);
    
        add_edge(0, 1, road_info{15, false}, roadMap);
        add_edge(1, 2, road_info{3, true}, roadMap);
    
        print_graph(roadMap);
    
        auto info_map = get(boost::road_info, roadMap);
    
        for (Edge e : boost::make_iterator_range(edges(roadMap))) {
            road_info& info = info_map[e];
            // or even easier
            // road_info& info = roadMap[e];
            auto& [miles, pothole] = info; // c++17
    
            std::cout << "Edge " << e << " " << miles << " miles, "
                      << "pothole:" << std::boolalpha << pothole << "\n";
        }
    
        // individual maps
        auto miles_map = boost::make_transform_value_property_map(
            std::mem_fn(&road_info::miles), info_map);
        auto poth_map = boost::make_transform_value_property_map(
            std::mem_fn(&road_info::pothole), info_map);
    
        for (Edge e : boost::make_iterator_range(edges(roadMap))) {
            std::cout << "Edge " << e << " " << miles_map[e] << " miles, "
                      << "pothole:" << std::boolalpha << poth_map[e] << "\n";
    
            put(poth_map, e, false); // reset pothole
        }
    }
    

    再次打印相同的输出。

    【讨论】:

    • 添加了使用各种结构的单个成员属性映射访问演示
    • 我相信 struct road_details_t 和 typedef 属性 是 BOOST_INSTALL_PROPERTY 的替代品,但我已经尝试了这两种方法,但都不适合我。编辑以将其包含为评论。
    • 看来例子是给顶点附加属性,road_info属于边
    • 首先,不,那不是替代方案。其次,我按照你自己的示例 typedef 使用 road_info 作为顶点属性。所以你的问题或 “road_info 属于边” 都是一个谎言:) 只需将它用于然后边缘:coliru.stacked-crooked.com/a/012508ccac9468ee(99% 相同的代码)
    • 如果没有elusive third approach,我当然不会放过你。请参阅“受虐狂”下的编辑答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 2019-09-11
    • 2011-12-18
    • 2019-09-14
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    相关资源
    最近更新 更多