Boost Graph Library: why are `source()`/`target()` non-member functions?
Why is `source()` not a member function of the graph class? (See http://www.boost.org/doc/libs/1_53_0/libs/graph/doc/index.html for description, and boost/graph/adjacency_list.hpp and boost/graph/adjacency_matrix.hpp for implementation.) I thought the reason was that it was a generic algorithm. But it's not. There's no interface (beyond `source()` itself) exposed by an edge descriptor that would allow `source()` to avoid digging into the implementation. If I interpret the code correctly, `source()` reads directly from the internal data structure of the edge descriptor (`detail::edge_base.m_source`), which I am pretty sure is the implementation detail (`m_source`, despite being strangely exposed as a public member, is not the standard interface; otherwise it would have been defined as such, and `source()` would have been entirely redundant.) I think I'm missing something, but what?
On Fri, 19 Apr 2013, Max Moroz wrote:
Why is `source()` not a member function of the graph class?
(See http://www.boost.org/doc/libs/1_53_0/libs/graph/doc/index.html for description, and boost/graph/adjacency_list.hpp and boost/graph/adjacency_matrix.hpp for implementation.)
I thought the reason was that it was a generic algorithm. But it's not. There's no interface (beyond `source()` itself) exposed by an edge descriptor that would allow `source()` to avoid digging into the implementation.
If I interpret the code correctly, `source()` reads directly from the internal data structure of the edge descriptor (`detail::edge_base.m_source`), which I am pretty sure is the implementation detail (`m_source`, despite being strangely exposed as a public member, is not the standard interface; otherwise it would have been defined as such, and `source()` would have been entirely redundant.)
I think I'm missing something, but what?
The point of using free functions is to allow them to be defined separately from the classes they relate to. For example, you could have a preexisting graph class that has differently-named members (say "edge.start()" and "edge.end()") and wrap free functions around those without changing the original code. However, you cannot add member functions to a class without modifying it. Thus, Boost.Graph uses free functions whenever possible to allow adaptation of third-party libraries just by adding new definitions, without changing any of the existing library code. -- Jeremiah Willcock
Jeremiah - Thank you - this is exactly what I was missing. Suppose I have a class DS that I want to adapt for use with Boost.Graph in two different ways (for example, what is considered a source in one adaptation should be a target in the other adaptation, and vice versa). How can I create two functions source() without causing a name conflict? Don't they both have to be defined in the boost namespace, with the same signatures? Also, I was wondering about an alternative approach. The interface could require member functions, and to provide them I would put third-party objects inside a wrapper class. The wrapper class would take the third-party object as a constructor argument, save it as a private data member, and expose the required interface in the form of member functions. This seems to allow for better encapsulation than the free functions approach. Does the free function approach have any advantages over the wrapper approach? Max
On Mon, 22 Apr 2013, Max Moroz wrote:
Jeremiah -
Thank you - this is exactly what I was missing.
Suppose I have a class DS that I want to adapt for use with Boost.Graph in two different ways (for example, what is considered a source in one adaptation should be a target in the other adaptation, and vice versa). How can I create two functions source() without causing a name conflict? Don't they both have to be defined in the boost namespace, with the same signatures?
You would need a wrapper class for that case, or else only wrap one direction and use reverse_graph to get the other one (it does the flipping of sources and targets for you).
Also, I was wondering about an alternative approach. The interface could require member functions, and to provide them I would put third-party objects inside a wrapper class. The wrapper class would take the third-party object as a constructor argument, save it as a private data member, and expose the required interface in the form of member functions. This seems to allow for better encapsulation than the free functions approach. Does the free function approach have any advantages over the wrapper approach?
Probably not too many, but it is more elegant in some cases to be able to pass in a third-party graph type as is into Boost.Graph algorithms. -- Jeremiah Willcock
participants (2)
-
Jeremiah Willcock
-
Max Moroz