
This question doesn't pertain to a particular Boost library, so I apologize if it's too off-topic for the list. According to http://www.boost.org/more/generic_programming.html , the Associated Types of a Concept are either nested in the class definition, or accessed through a traits class. With iterators, traits were used because you can't nest typedefs within pointers, which are valid iterators in many cases. With containers (such as AssociativeContainerConcept), the Concepts require nested typedefs. My questions are - when creating your own library, when do you choose nested typedefs over a traits class, and what effects does it have on a library user, both from a usability and extensibility point of view. What questions do you ask yourself to come to a decision? Thanks, --Michael Fawcett

Michael Fawcett <michael.fawcett <at> gmail.com> writes:
My questions are - when creating your own library, when do you choose nested typedefs over a traits class, and what effects does it have on a library user, both from a usability and extensibility point of view. What questions do you ask yourself to come to a decision?
It's regular Policies vs. Traits decision. If your typedefs can't be arbutrary combined with primary template parameter type use concepts with typedefs if these are just secondary types *uniqly* identified by your primary template parameter use traits. Usually decision is clear. Gennadiy

On Jan 10, 2008, at 3:40 PM, Michael Fawcett wrote:
My questions are - when creating your own library, when do you choose nested typedefs over a traits class,
Never.
and what effects does it have on a library user, both from a usability and extensibility point of view. What questions do you ask yourself to come to a decision?
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify. - Doug

Doug Gregor <dgregor <at> osl.iu.edu> writes:
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify.
This is not exactly true if typedefs are part of some concepts types used as a policies, right? This is true is we are talking about typedefs as members of your primary template parameter type.

On Jan 10, 2008, at 4:54 PM, Gennadiy Rozental wrote:
Doug Gregor <dgregor <at> osl.iu.edu> writes:
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify.
This is not exactly true if typedefs are part of some concepts types used as a policies, right? This is true is we are talking about typedefs as members of your primary template parameter type.
With policies... sure, nested types are okay there, because you typically have to code each policy class for a specific template anyway (and there generally isn't reuse of policies across libraries). - Doug

On Jan 10, 2008 4:43 PM, Doug Gregor <dgregor@osl.iu.edu> wrote:
On Jan 10, 2008, at 3:40 PM, Michael Fawcett wrote:
My questions are - when creating your own library, when do you choose nested typedefs over a traits class,
Never.
and what effects does it have on a library user, both from a usability and extensibility point of view. What questions do you ask yourself to come to a decision?
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify.
From the following possible (contrived) implementations of retrieving data from a Node, which would be best in your opinion (where Node Concept is basically some pointers to other Nodes plus data, like a
This has always been my thinking, but to play devil's advocate, what about providing an adapter class around that type that you can't modify? To answer my own question, it provides a much larger burden on the user of your library (wrap all instances with wrapper type when using my generic algorithms vs. specializing one traits struct). linked list node, or tree node)? // Only Concept template <typename T> typename T::data_type *data(T *node) { boost::function_requires<NodeConcept>(); return node->data; } // Partial traits template <typename T> typename node_traits<T>::data_type *data(T *node) { boost::function_requires<NodeConcept>(); return node->data; } // Completely traits template <typename T> typename node_traits<T>::data_type *data(T *node) { boost::function_requires<NodeConcept>(); return slist_node_traits<T>::data(node) } and, if you suggest using all traits, would Node Concept be reduced to simply checking to see if node_traits was fully specialized? --Michael Fawcett

On Thu, 2008-01-10 at 16:57 -0500, Michael Fawcett wrote:
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify.
This has always been my thinking, but to play devil's advocate, what about providing an adapter class around that type that you can't modify? To answer my own question, it provides a much larger burden on the user of your library (wrap all instances with wrapper type when using my generic algorithms vs. specializing one traits struct).
Exactly. An adapter class is always an option (whether or not the generic algorithm uses traits), but it's a last-resort option because it often requires more code and more maintenance than specializing traits.
// Completely traits template <typename T> typename node_traits<T>::data_type *data(T *node) { boost::function_requires<NodeConcept>(); return slist_node_traits<T>::data(node) }
and, if you suggest using all traits, would Node Concept be reduced to simply checking to see if node_traits was fully specialized?
Using all traits is extremely hard, because so many things happen implictly in C++---implicit conversions, copy constructions, etc.. But yes, if you used only traits, checking for a fully specialized node_traits (with the right signatures!) would work. - Doug

Doug Gregor wrote:
On Jan 10, 2008, at 3:40 PM, Michael Fawcett wrote:
My questions are - when creating your own library, when do you choose nested typedefs over a traits class,
Never.
and what effects does it have on a library user, both from a usability and extensibility point of view. What questions do you ask yourself to come to a decision?
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify.
Type members are inherited by derived classes -- traits specializations are not. So I don't think it's generally true that either one is better than the other -- it depends on what you're trying to achieve. Regards, Tobias

On Fri, 2008-01-11 at 12:25 +0100, Tobias Schwinger wrote:
Doug Gregor wrote:
On Jan 10, 2008, at 3:40 PM, Michael Fawcett wrote:
My questions are - when creating your own library, when do you choose nested typedefs over a traits class,
Never.
and what effects does it have on a library user, both from a usability and extensibility point of view. What questions do you ask yourself to come to a decision?
Traits classes are far better than nested typedefs, because one can always specialize a traits class for an existing type---even if you can't modify the type because it is built-in or comes from another library that you can't modify.
Type members are inherited by derived classes -- traits specializations are not. So I don't think it's generally true that either one is better than the other -- it depends on what you're trying to achieve.
From the standpoint of a generic algorithm, the use of traits allows the widest variety of existing data types to work with the generic algorithm without modifying the data types themselves.
Besides, it's typical for the primary templates of traits to fall back to nested types, which solves the ease-of-use problem for derived classes (and for most classes, in fact), while still giving the flexibility of traits, e.g., template<typename Iter> struct iterator_traits { typedef typename Iter::iterator_category iterator_category; typedef typename Iter::value_type value_type; typedef typename Iter::reference reference; typedef typename Iter::pointer pointer; typedef typename Iter::difference_type difference_type; }; - Doug

On Jan 11, 2008 8:40 AM, Douglas Gregor <dgregor@osl.iu.edu> wrote:
Besides, it's typical for the primary templates of traits to fall back to nested types, which solves the ease-of-use problem for derived classes (and for most classes, in fact), while still giving the flexibility of traits, e.g.,
template<typename Iter> struct iterator_traits { typedef typename Iter::iterator_category iterator_category; typedef typename Iter::value_type value_type; typedef typename Iter::reference reference; typedef typename Iter::pointer pointer; typedef typename Iter::difference_type difference_type; };
That is the approach I have been taking. Doug, Tobias, Gennadiy, thank you all for this very educational (for me) discussion. I think I understand all of the deciding factors much better now. --Michael Fawcett
participants (5)
-
Doug Gregor
-
Douglas Gregor
-
Gennadiy Rozental
-
Michael Fawcett
-
Tobias Schwinger