简体   繁体   English

具有多态顶点和边的 boost::adjancency_list

[英]boost::adjancency_list with polymorphic vertices and edges

I'm learning about BGL and pieced together a program (from various related StackOverflow answers) to build a graph that should be able to deal with polymorphic vertices and edges.我正在学习 BGL 并拼凑一个程序(来自各种相关的 StackOverflow 答案)来构建一个应该能够处理多态顶点和边的图形。 The graph should be able to handle parallel edges (of the same type or not).该图应该能够处理平行边(相同类型或不同类型)。 I'd like to get dijkstra_shortest_paths from a starting vertex_descriptor to: 1. all Park's;我想将dijkstra_shortest_paths从起始vertex_descriptor到: 1. 所有 Park 的; 2. all Park's and City's; 2. 所有公园和城市的; 3. all City's via Railway only. 3. 仅限全市的铁路。 Is that even possible?这甚至可能吗? Thank you.谢谢你。

#include <iostream>
#include <deque>

#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/topological_sort.hpp"
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/variant.hpp>


#include <boost/graph/adj_list_serialize.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>

using namespace boost;

struct Highway {
  std::string name;
  int miles;
  int speed_limit;
  int lanes;
  bool divided;
};

struct Railway { 
  std::string name; 
  int km;
};

struct City {
  std::string name;
  int population;
  std::vector<int> zipcodes;
};

struct Park {
  std::string name;
  int population;
  std::vector<int> zipcodes;
};


struct Country {
  std::string name;
  bool use_right;   // Drive on the left or right
  bool use_metric;  // mph or km/h
};

int main(int argc, char *argv[]) {

  typedef boost::adjacency_list<
    boost::listS, boost::vecS, boost::bidirectionalS,
    boost::variant<City, Park>, boost::variant<Highway, Railway>, Country>
      Map;

  City c1 = {"San Jose", 1200000, {95126, 95128}};
  City c2 = {"San Francisco", 500000, {95008, 95009}};
  City c3 = {"Santa Cruz", 300000, {94001, 94002}};
  City c4 = {"Oakland", 800000, {93001, 93002}};
  City c5 = {"San Diego", 2800000, {92001, 92002}};

  Map map; // load the map
  Map::vertex_descriptor c1d = boost::add_vertex(c1, map),
    c2d = boost::add_vertex(c2, map),
    c3d = boost::add_vertex(c3, map),
    c4d = boost::add_vertex(c4, map), 
    c5d = boost::add_vertex(c5, map); 
  add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
  add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
  add_edge(c2d, c3d, Highway{"92", 13, 60, 1, true}, map);
  //add_edge(c2, c3, map);
  //add_edge(c1, c3, map);

  map[graph_bundle].name = "United States";
  map[graph_bundle].use_right = true;
  map[graph_bundle].use_metric = false;

  using vertex_descriptor = boost::graph_traits<Map>::vertex_descriptor;

  Map::vertex_descriptor from = *vertices(map).first;
  std::vector<int> distances(num_vertices(map));
  auto v_index = get(boost::vertex_index, map);
  auto v_bundle = get(boost::vertex_bundle, map);
  dijkstra_shortest_paths(map, from,
      weight_map(get(get(&Highway::miles, map))
      .distance_map(make_iterator_property_map(distances.begin(),
      get(vertex_index, map))));
  std::cout << "distances and parents:" << std::endl;
  graph_traits <Map>::vertex_iterator vi, vend;
  for (boost::tie(vi, vend) = vertices(map); vi != vend; ++vi) {
    std::cout << "distance(" << get<City>(&City::name, map[*vi]) << ") = " << distances[*vi] << ", " << "\n";
  }
  std::cout << std::endl;

  return 0;
}

So, in short, you want to access a property from a variant element type.因此,简而言之,您希望从变体元素类型访问属性。 You could make your own property map, facilitate your own set of accessors or use a property map adaptor.您可以创建自己的属性 map,方便您自己的访问器集使用属性 map 适配器。

The latter being least work:后者工作最少:

auto highway_miles = boost::make_transform_value_property_map(
    [](Interconnect const& conn) {
        if (auto* c = boost::get<Highway>(&conn)) {
            return c->miles;
        } else {
            return std::numeric_limits<Distance>::max();
        }
    },
    get(boost::edge_bundle, map));

dijkstra_shortest_paths(
    map, from,
    weight_map(highway_miles)
        .distance_map(make_iterator_property_map(
            distances.begin(), v_index)));

In fact with vecS chosen vertex container selector, you could write it more succinctly:事实上,使用vecS选择的顶点容器选择器,您可以更简洁地编写它:

dijkstra_shortest_paths(
    map, from, weight_map(highway_miles).distance_map(distances.data()));

Live Demo现场演示

Live on Compiler Explorer实时编译器资源管理器

#include <deque>
#include <iostream>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/variant.hpp>

using Distance = int;

struct Highway {
    std::string name;
    Distance    miles;
    unsigned    speed_limit, lanes;
    bool        divided;
};

struct Railway {
    std::string name;
    Distance    km;
};

struct City {
    std::string           name;
    unsigned              population;
    std::vector<unsigned> zipcodes;
};

struct Park {
    std::string           name;
    unsigned              population;
    std::vector<unsigned> zipcodes;
};

using Interconnect = boost::variant<Highway, Railway>;
using Area = boost::variant<City, Park>;

enum class RoadOrientation { right, left };
enum class UnitSystem { metric, imperial };

struct Country {
    std::string     name;
    RoadOrientation driving;
    UnitSystem      measure;
};

using Map = boost::adjacency_list<                    //
    boost::listS, boost::vecS, boost::bidirectionalS, //
    Area, Interconnect, Country>;

int main()
{
    Map map;
    map[boost::graph_bundle] = {
        "United States",
        RoadOrientation::right,
        UnitSystem::imperial,
    };

    auto c1d = add_vertex(City{"San Jose",      1200000, {95126, 95128}}, map);
    auto c2d = add_vertex(City{"San Francisco", 500000,  {95008, 95009}}, map);
    auto c3d = add_vertex(City{"Santa Cruz",    300000,  {94001, 94002}}, map);
               add_vertex(City{"Oakland",       800000,  {93001, 93002}}, map);
               add_vertex(City{"San Diego",     2800000, {92001, 92002}}, map);

    add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
    add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
    add_edge(c2d, c3d, Highway{"92",  13, 60, 1, true}, map);

    using V = Map::vertex_descriptor;

    V                from = vertex(0, map);
    std::vector<int> distances(num_vertices(map));

    auto v_index       = get(boost::vertex_index, map);
    auto highway_miles = boost::make_transform_value_property_map(
        [](Interconnect const& conn) {
            if (auto* c = boost::get<Highway>(&conn)) {
                return c->miles;
            } else {
                return std::numeric_limits<Distance>::max();
            }
        },
        get(boost::edge_bundle, map));

    dijkstra_shortest_paths(
        map, from,
        weight_map(highway_miles)
            .distance_map(make_iterator_property_map(
                distances.begin(), v_index)));

    dijkstra_shortest_paths(
        map, from, weight_map(highway_miles).distance_map(distances.data()));

    std::cout << "distances and parents:" << std::endl;

    auto name_of = [](auto const& area) {
        return boost::apply_visitor(
            [](auto const& c_or_p) { return c_or_p.name; }, area);
    };

    for (auto v : boost::make_iterator_range(vertices(map)))
        std::cout << "distance(" << name_of(map[v]) << ") = " << distances[v] << "\n";
}

Prints印刷

distances and parents:
distance(San Jose) = 0
distance(San Francisco) = 42
distance(Santa Cruz) = 55
distance(Oakland) = 2147483647
distance(San Diego) = 2147483647

More Ideas更多创意

I've provided recommendations/alternatives in the past:我过去曾提供过建议/替代方案:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM