Boost logo

Boost :

From: Aleksey Gurtovoy (alexy_at_[hidden])
Date: 2001-06-04 03:52:57


Vesa Karvonen wrote:
> I'm currently writing metacode for my resource library that I
> hope to be able to offer to Boost next week. I find that the
> Boost metaprogramming and type traits facilities are not
> mature enough for my taste, so I have ported a mini version
> of our metaprogramming facilities to Boost conventions.

'boost::mpl' library is indeed immature (and it's not an official boost
library yet), but I wouldn't say that about the rest.

>
> call_traits
> ===========
>
> The call_traits<> is basically a mini-BLOB (antipattern).
> Specifically the call_traits<> template is four functions in one.

And 'std::iterator_traits<>' is 5 "functions" in one, and
'std::char_traits<>' is 19 "functions" in one :). I don't use
'std::char_traits<>' very often, but I do use 'std::iterator_traits<>' a lot
and personally I wouldn't appreciate, for example, migration from
'std::iterator_traits<T>::iterator_category' form to
'std::iterator_traits::iterator_category<T>::type'. The latter form requires
more typing, is less clear (IMO), and does not gain you anything in terms of
compile-time dependencies/compilation time unless you are willing to put
every (former) 'iterator_traits<>' member into his own separate header. Even
in that case, for this particular example and typical usage of
'iterator_traits<>', I would argue that such refactoring is most likely to
_increase_ compilation time (and also make library's maintenance harder than
it should be), because most clients would #include most of the
'iterator_traits' headers anyway, and instead of (typically) one primary
'iterator_traits<>' class template and two its partial specializations
('iterator_traits<T*>' and 'iterator_traits<T const*>') they would get 5
templates + 10 partial specialization; the consequences are that with the
current standard library's 'iterator_traits<>' approach instantiation of
(straightforward implementation of) 'std::vector<T>' with 10 different T
will result in 20 full (implicitly instantiated) specialization of
'iterator_traits<>' class template vs. 100 full specializations of the
hypothetical 'iterator_traits::iterator_category<>',
'iterator_traits::value_type<>' templates etc. For me this 5 times
difference in number of generated classes the compiler/linker has to deal
with is kind of scary.

How all this relates to 'call_traits<>'? Well, my point is that the fact
that number of "functions" in traits class > 1 does not per se identify a
Blob antipattern (BTW, if you're wondering what Blob is about, here is a
link - http://www.antipatterns.com/briefing/tsld024.htm). There are both
technical and conceptual reasons why one would like to bind several traits
together in one class (after all, they are closely related "functions",
right?); basically, given the above 'iterator_traits<>' example, I think
that the decision "to split or not to split" should be heavily influenced
by:

1) the most often encountered usage patterns (how often these traits are
used together);
2) fact of (not) having a common for all traits implementation's variability
point; clearly, in case of 'iterator_traits<>' such point exists ('T' vs.
'T*' and 'T const*') and this is what makes splitting them into separate
classes unfeasible; I believe that this applies to 'call_traits<>' as well.

So although I am not completely sure about (1) in application to
'call_traits<>', IMO the issue is not so black & white, and personally I
tend to think that the current 'call_traits' approach is ok.

> It is preferable to have simple primitive template
> metafunctions, because:
>
> 1. Such metafunctions can be made to use standard interfaces
> rather than
> the special case interface of call_traits.

I am not sure I understand this one. Could you elaborate, please?

> 2. Primitive metafunctions are easier to use, understand and
> port (fact of
> life). For instance, should one little thing fail to compile in call
> traits, you might not be able to port the whole thing.

Depends on (2) from the above list.

> 3. Primitive metafunctions scale better in large scale
> development (with a
> BLOB you need to include everything whether or not you need
> it). (In fact,
> currently I only need param_type.)

True, but it's not obvious that 'call_traits<>' is an example of Blob.

[snip]

> The above metafunctions are clearly superior to use in
> metacode than the
> call_traits<> template. I can provide examples if you don't trust me.

Of course we do trust each other experience. But examples are almost never
superfluous as they help other people to develop their own understanding of
the issues.

>
> arithmetic_traits
> =================
>
> The boost/arithmetic_traits.hpp can be simplified by a factor of ~4.
> Currently the arithmetic types are repeated 4 times:
> - as non-cv types,
> - as const types,
> - as volatile types, and
> - as const volatile types.
>
> A smarter implementation would look something like this:
>
> namespace detail
> {
> typedef
> make_typelist
> < const volatile char
> , const volatile signed char
> , const volatile unsigned char
> , const volatile short
> ,...
> >::type const_volatile_arithmetic_types;
>
> template<class T>
> struct type_const_volatile
> { typedef const volatile T
> type;
> };
>
> template<class T>
> struct type_identity
> { typedef T
> type;
> };
> }
>
> template<class T>
> struct is_arithmetic
> {
> enum
> { value =
> typelist_has
> < detail::const_volatile_arithmetic_types
> , typename type_inner_if
> < is_reference<T>::value
> , detail::type_identity<T>
> , detail::type_const_volatile<T>
> >::type
> >::value;
> };
> };

Yes, comparing the amount of code required to implement the same
functionality, something like that is clearly superior to "traditional"
approach. But on the other hand, implementing something as simple as
'is_arithmetic' class template using type_lists & co. would impose a _huge_
compilation time penalty on the library users, at least given the current
state of compiler technology, as well as some of other undesirable effects
we were trying to avoid (see
http://groups.yahoo.com/group/boost/message/7813, for example). So, while
being a big fan of compile-time operations on type and value sequences (as
you can guess from boost::mpl), I still would be against such change. After
all, it's only about library's implementation, and there are other ways to
reduce code "repetition" (e.g. using your PREPROCESSOR library :).

>
> MPL
> ===
>
> Technical Issues in Boost MPL:
> - The current code is not completely ported to MSVC++, so I
> can not use it.

It wasn't ported, it was _written_ using MSVC :) (well, actually, using all
the following: MSVC6.4, Comeau 4.2.44 and Metrowerks 6.1). All the tests
compile fine on my machine (you need to specify something like /Zm1000 in
MSVC's project settings, though). Could you give a specific description of
the problems you are having?

> - The factory template uses O(N*N) tokens, which is not optimal. It is
> possible to do with only O(N) tokens, which can significantly reduce
> compilation times.

I am not sure I understand. Could you give an example?

> - The code makes no use of private or public to hide implementation
> details or hilite the interfaces of metacode. This makes it more
> difficult to understand the code.

Valid point. I need to discipline myself more :).

> - What is the purpose of the SequenceCategory and TraitsType template
> parameters of list_node? I find that those complicate the
> implementation significantly with little or no benefit over pure, cons
> based, proper typelists. Can you give a concrete examples that would
> demonstrate the superiority of the MPL technique?

I think I can, but that's a topic for another email. I'll try to do it
tomorrow (it's kind of late here now :).

> - list_node<> doesn't check the validity of the template arguments. At
> least NextNode has special requirements. This same problem
> can be seen in many places.

Right, for now boost::mpl doesn't perform any concept checking. It's on my
to do list, but not in the top 10 items. If you think it's more important
than that, I would be glad to accept your help :).

> - What is wrong with using enum {} when there is no
> particular reason to have a specific type? Does it fail on some broken
> compilers? Is the use of BOOST_STATIC_CONSTANT() always worth it?
> IIRC even Bjarne Stroustrup has commented that static class constants are
> a misfeature.

I don't like enum trick either. IMO it's ugly, error prone and clutters the
code. Also, IMO the ability to specify a type of the constant is a good
thing.

> - The endline layout is a bad idea. It makes maintanance more
> difficult. I can already pinpoint examples of broken code layout in MPL.
> See Code Complete, ISBN 1-55615-484-4, for details.

I've read the book. I don't think that the 'boost::mpl' code you're
referring to does have any of the "endline layout" problems. If it does, I
would
appreciate being enlightened.

> IMO Boost should
> have a guideline that says that library authors are recommend not to
> use endline layout, because it is not maintainable.

May be. Depends on what do you mean by "endline layout". For now I think
'boost::mpl' code you are referring to is perfectly ok in this respect, so I
would be against including guidelines that mark such layout as "bad". BTW,
so far formatting issues were viewed as personal preferences and probably,
except corner cases, they should stay this way.

> - MPL should be separated into multiple more cohesive libraries.

Agree.

> For example:
> - type traits (is_same, is_convertible, ...)
> - actually Boost already has this library and I don't see much value
> in duplicating the library.

There is no duplication. 'boost::mpl' does use type traits library, and I
don't know why you got the impression about duplicating of the
functionality. The library has the 'mpl::same_as' predicate class (which
uses the type traits 'is_same' class template as well), but that's a
different story.

> - template metaprogramming (if, switch, for, while, ...)
> - metadata-structures (lists, trees, etc...)

> - I think that a lot could be learned by using existing
> knowledge on functional programming. For example:
> - Chris Okasaki: Purely Functional Data Structures,
> ISBN 0521663504

Thanks for the hint :).

Aleksey


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