| 57 | The "most" precise way to actually implement these is as follows. First, we need a type that actually performs the mapping - more or less. Essentially, this is a container that overloads the `operator[]` operator and the index is either an edge or vertex descriptor and the value is... well, dependant upon the mapping. This is the primary mapping object for the vertex/edge-to-value object. The important thing about this type is that it can add overhead to algorithms if the mapping is poorly chosen. For example, `std::map` is probably a poor choice because its lookup (mapping) is logarithmic (not constant). |
| 58 | |
| 59 | If we're talking about the typical vector-based vertex storage, then this is what we might do (for a distance map, per-se). |
| 60 | |
| 61 | {{{ |
| 62 | #!cpp |
| 63 | typedef std::vector<int> DistanceMapping; |
| 64 | }}} |
| 65 | |
| 66 | Here, the `int` refers to the distance type and not the key type (descriptor). Because it's a vector, the key type has to be an integer type (probably unsigned). This works out great for vector-based vertex storage since descriptors magically happen to be unsigned integers. Its even better since the value of those descriptors magically align to the range [0, `num_vertices(g)`) so the mapping is trivial, and very, very fast (probably the fastest you can get). |
| 67 | |
| 68 | The second thing we ''really'' need is an adaptor between the mapping type and the Boost.PropertyMap library. Fortunately, there are already a bunch of these - for this application, we're looking at an `iterator_property_map`. This is an iterator property map because the indices of vector can also act as iterators to the container (as pinters). The only thing we have to do is define the types responsible for the actual mapping and adapt that to the library. This is done with: |
| 69 | |
| 70 | {{{ |
| 71 | #!cpp |
| 72 | typedef iterator_property_map<DistanceMapping::iterator, |
| 73 | property_map<Graph, vertex_index_t>::type> DistanceMap; |
| 74 | }}} |
| 75 | |
| 76 | That's plenty ugly, but it's actually correct. The first parameter says that the adapted property map uses the mapping objects iterator type for indexing values. Remember that this iterator type is just an unsigned integer - the same as the vertex_descriptor type. The second parameter actually tells the adapter how to translate from a `vertex_descriptor` type to a the distance iterator. This parameter is actually defined in terms of the graph for which the map is being defined. Although we don't really see it here, this entire declaration is basically defining a set of functions that does this: |
| 77 | |
| 78 | {{{ |
| 79 | #!cpp |
| 80 | int // the distance type (value) |
| 81 | get(const DistanceMaping& map, // the underlying mapping object (the vector) |
| 82 | unsigned v) // the vertex descriptor of vector-based vertex storage |
| 83 | { |
| 84 | return map[v]; |
| 85 | } |
| 86 | }}} |
| 87 | |
| 88 | This is fundamentally what happens. The actual series of type transformations and function instantiation is a lot more complicated, but at the heart of the operation is a constant-time vector access. Unfortunately, if your descriptor isn't an unsigned integer in the range [0, num_vertices(g)), you'll have to read the next section. |
| 89 | |
| 90 | It might also be worth pointing out that when you see code that looks like this: |
| 91 | |
| 92 | {{{ |
| 93 | #!cpp |
| 94 | dijkstra_shortest_paths(g, v, distance_map(&dist[0])); |
| 95 | }}} |
| 96 | |
| 97 | that `distance_map(&dists[0])` is actually building an `iterator_property_map` just like before. It just so happens that `&dists[0]` is a valid iterator type for a vector so the construction of the property map is completely hidden. |
| 98 | |
| 154 | |
| 155 | == Helper Functions == |
| 156 | It would be nice if there were a suite of functions and types that would help programmers declare exterior property maps. I think that these would essentially default to either vectors or unordered maps depending on how the graph is defined. It might be helpful to have something like: |
| 157 | |
| 158 | {{{ |
| 159 | #!cpp |
| 160 | typedef exterior_vertex_property<Graph, float>::property_type DistanceValues; |
| 161 | typedef exterior_vertrex_property<Graph, float>::mapping_type DistanceMap; |
| 162 | |
| 163 | DistanceValues dists(num_vertices(g)); |
| 164 | DistanceMap dist_map(dists); |
| 165 | }}} |
| 166 | |
| 167 | Essentially this template is specialized for vector- and non-vector-based storage such that the proeprty type is vectors for vector-based and unordered_maps for non-vector based. The mapping type is either an associative property map or an iterator property map. |
| 168 | |
| 169 | An alternative approach migth be to build a single type that exposes references to the values and the mapping as inline functions: |
| 170 | |
| 171 | {{{ |
| 172 | #!cpp |
| 173 | typedef exterior_vertex_property<Graph, float> DistanceProperty; |
| 174 | |
| 175 | DistanceProperty dists(num_vertices(g)); |
| 176 | dijkstra_shortest_paths(g, v, distance_map(dists.map())); |
| 177 | }}} |
| 178 | |
| 179 | Again... maybe. |