Boost logo

Boost :

From: Aleksey Gurtovoy (alexy_at_[hidden])
Date: 2001-08-16 06:25:34


John Max Skaller wrote:
> Aleksey Gurtovoy wrote:
> >
> > John Max Skaller wrote:
> > > Now, I HATE traits libraries, (template equivalent
> > > of global data .. )
> >
> > "Traits" class templates in type_traits library are not in
> > any way an equivalent of global data; they are compile-time
> > "functions" than operate on types; so there is no reason for
> > hate :).
>
> Yes there is: these 'functions' generally consist of
> a large set of specialisations, and they fail to work
> immediately you supply an argument for which there is no
> specialisation, so you have to 'augment' the function
> with a suitable specialisation.

If you are talking about the traits technique in general - yes, in many
cases such situation definitely takes place (and it's not always bad, see
below). But I was talking about "traits" templates in the type_traits
library, and the above paragraph certainly does not apply to them. You are
not supposed to specialize most of type_traits templates (30 out of 37) (in
ideal world, of course). It just wouldn't make any sense (to specialize of
'remove_const'? 'is_fundamental'? 'is_convertible'?), and on conforming
compiler they will work "out of box" with any type, built-in or
user-defined. There are a few templates that cannot be implemented within
the current language, and thus require a compiler support, or, in absence of
this, user-defined specializations:

is_union
is_class
is_POD
has_trivial_constructor
has_trivial_copy
has_trivial_assign
has_trivial_destructor

but again, because these templates describe very fundamental properties of
types, and because it's very easy for the compiler to support those (it
already has all the knowledge, it just doesn't share it with us ;), it's
just a matter of time when the library will become completely free from the
typical drawback of the traits technique you've described above. (BTW, on
some of the compilers we're already there). And (plans for) standardization
could definitely speed up this process :).

> Such specialisations
> are effectively global data: the template is in NO WAY
> generic, it is in fact a huge switch over global case data.

I understood this analogy from the very beginning, I just don't think that
what the above paragraph says applies to the class templates provided by the
type_traits library.

> An example is the trait which tells you which character
> of a type is the '\0' equivalent.
>
> This is really bad style. To add a new type T which
> is to act like a character, you have to modify a completely
> unrelated template with a specialisation: you're actually
> extending something in the sacred 'std' namespace.

Well, not necessarily (I mean, it doesn't have to be this way). For example,
default implementation of 'std::char_traits' template could look like this:

template<typename T>
struct char_traits
{
    typedef T char_type;
    typedef typename T::int_type int_type;
    
    // ...
    static size_t length(char_type const* s)
    {
        return T::length(s);
    }
    
    static int_type eof()
    {
        return T::eof();
    }
};

and then you could provide all these functions and typedefs as a part of
your hypothetical character class interface. Would this make you feel
better?

> Contrast this fragile lunacy with the module system
> used in SML and you'll see why I hate traits so much:
> traits are the enemy of modular design -- you're forced to
> scatter your 'module' all over the place to make it work.
> Again, implicit coupling.

Actually, the fact that most often traits are not intrusive can be viewed as
an advantage of the technique. First at all, there are always built-in
types, and global traits are the only way to associate library-specific
information with them. Secondly, many libraries need to be generic enough to
not force their users to change the interfaces of the already existing
classes in order to use them together with the library components, and
again, traits technique is probably the most straightforward solution here.
Of course the drawbacks you are talking about are real too, I am just
pointing out that often advantages overweight disadvantages, and that
generally non-intrusiveness is considered as a good thing.

> As an example of what can go wrong:
> I make a type UNICODE which is kind of cute. But i don't
> play the traits game. So you can't use it with basic_string/
> stream io, you need the trait for 'null char' defined for that.
> So you go and add the specialisation.
>
> Woops. Joe, Fred, Samantha, and Alex all had the same
> idea; the ODR is broken if you try to combine your code.

It's not very different from, for example, a situation when they would all
at the same time came to the conclusion that the class needs to have an
output stream operator ("std::ostream& operator<<(std::ostream&, UNICODE)"),
is it? :)

> [My argument doesn't apply to genuinely polymorphic
> templates used as 'type functions', that is, ones which
> are parametrically polymorphic. But the ones with lots
> of specialisations are just a huge hack for the lack
> of a proper concept of unification, both at the
> executable and tempate level]

Actually, I do share this point of view, or let's say that I tend to agree.
I just don't think that (1) the issue is so black & white, and (2) that the
type_traits library falls under category of "huge hacks" :).

Aleksey


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk