Boost logo

Boost :

From: Vesa Karvonen (vesa.karvonen_at_[hidden])
Date: 2001-06-06 09:10:30


From: "Aleksey Gurtovoy" <alexy_at_[hidden]>
[snip]
> Agree, in sense that 'param_type' is so fundamental (and, among all
other
> call_traits, most often used?) generic programming facility (at least
with
> the language as it is now :), that personally I would prefer to see it
as
> close to the first left :: as possible :). However,
>
> boost::call_traits::param_type<T>::type (vs.
> boost::call_traits<T>::param_type)
>
> form does not (syntacticaly) make things any better, does it?

The namespace call_traits, that I had in my original example can certainly
be dropped. However, the main reasons why I would like to see
call_traits<> split are:
- call_traits<> violates the Interface Segregation Principle (ISP). This
means that clients who are not interested in all services of call_traits<>
are forced to be dependent on those services.
- The compile time overhead of the resulting metafunctions can be made an
order of magnitude smaller than that of the current call_traits<>
template. (The cost of using ALL the services of call_traits<> will be
about the same, but obviously it can be slightly higher.)
- The resulting metafunctions, like param_type<>, can then use a standard
interface and we wouldn't have to use fancy 3-liners to combine them
flexibly in metacode.

> > Including <boost/call_traits.hpp> increased my current
> > compiling time by a factor of about 2 (from 1 to 2.8 seconds).
> > However, at this point my code is not complete, so the hit is
> > likely to become less significant (for a single test).
>
> Yes, I wouldn't describe this slowdown using factor notation; when your
> compile time come to 10 minutes, including 'call_traits.hpp' header
won't
> drop it further to 20, just to 21.8 :).

I agree. The difference between ~1 and ~3 seconds is noticeable. The
former feels instantaneous and the latter doesn't.

> > For the nearly trivial services of param_type, I find an
> > additional 1.8 seconds of compile time unacceptable.
>
> I understand your reasoning; however I think that the main source of
such
> slowdown is not the 'call_traits' library itself, but the type_traits
> library headers which as you might notice, almost all (or, in case of
MSVC
> etc., all) got included into it.

True. I did notice that when I first analyzed the reason for the 1.8
second slowdown (should have mentioned it too). The type_traits headers,
many of which aren't necessary for param_type, are the actual cause of the
slowdown.

> > Like said earlier, the standardized interface eliminates the
> > glue that is otherwise necessary.
[snip]
> Yes, but, as I said above, you can't possibly reduce everything to
'::type'
> interface;

Not everything of course, but when 95% of metacode uses the convention,
95% of the glue disappears.

I think that it is perfectly possible to have 95% of Boost metafunctions
to use the ::type interface. It should also be perfectly possible to have
95% of Boost "metafunction objects" to use a convention such as
::apply<args>.

> you need a more general technique to handle cases like

I like to think that a "general" technique is one that applies to a wide
range of problems with little work. Standardization is a general technique
that can eliminate nearly all of the 3-liners.

> typedef typename mpl::select_type<
> condition<T>::value
> , something<T>::fancy_type
> , T
> >::type type;
>
> where 'something<T>' cannot be instantiated in case if
'condition<T>::value'
> is false. My current approach to it, which is also used in 'boost::mpl',
is
> to write a simple 3-line 'faked_fancy_type' wrapper:
>
> template<typename T> struct faked_fancy_type {
> typedef T fancy_type;
> };
>
> and then the troublesome construct can be rewritten as:
>
> typedef typename mpl::select_type<
> condition
> , something<T>
> , faked_fancy_type<T>
> >::type::fancy_type type;

The above example is troublesome. On many compilers the ::type::fancy_type
occasionally (but not necessarily always) fails to compile.

My points were that when you standardize the metafunction returns:
- you don't need the fancy 3-liners.
- you can write metafacilities that enable you to avoid extra clutter.
=> this leads to simpler and more robust metacode.

For example, here is how we could write your example above when
(practically) everything uses the ::type standard:

    typedef typename select_inner_type
      < condition
      , something<T>
      , type_identity<T>
>::type type;

Please note that the above example does not use the troublesome
::type::type. The select_inner_type instantiates the chosen branch. Here
is a simplistic implementation (the one I'm using allows to select the
level of instantiation):

    template<bool c, class T, class E>
    struct select_inner_type
    {
      typedef typename
        type_inner< select_type<c, T, E> >::type
          type;
    };

    template<class fun>
    struct type_inner
    {
    private:
      // this is the type returned by select_type
      typedef typename fun::type
        fun_type;
    public:
      // this is the type returned by the type returned by select_type
      typedef typename fun_type;
        type;
    };

> > > > The boost/arithmetic_traits.hpp can be simplified by a
> > factor of ~4.
[snip]
> Yes, but "preprocessing" of the code is still a relatively fast
operation,
> comparing to the process of templates instantiation, especially
> instantiation of deeply recursive templates.

Reiterating my point, the ~4 times smaller implementation does not have to
be deeply recursive.

Regarding the speed of template instantiation, it is likely that it gets
relatively faster in the future.

> > The ::template syntax causes a lot of problems.
>
> I am willing to help, but I need a specific example that fails to
compile on
> your machine. BTW, what exactly version of MSVC do you use?

As I indicated earlier, I'm not currently using mpl, but our own
metaprogramming facilities. I'll retest the MPL later with my code and
post more specific problems.

We are using MSVC 6.0 sp 5 and also a particular beta version that is not
publicly available. Suffice to say that the subsets of C++ that those
compilers can handle are not the same.

> > I think that there is very little need for value lists,
> > because you can use a value wrapper type:
[snip]
> That's basically how 'value_list' is implemented. However, besides that
it
> has the "right" name, construction of it is also much more simple:
[snip]
> I think this is an important convenience, and I would like to keep it.

Yes, I agree that the factory method is nice. You can implement the
valuelist factory separately using O(N) tokens, so it is not a major
problem that you may not be able to reuse the typelist factory for the
purpose.

So, IMO the factory and having the "right" name, does not justify
complicating the typelist implementation with a sequence category and
traits. A much simpler typelist implementation, that can occasionally be
much more convenient to use with partial specialization, is to use a pure,
proper cons based typelist:

    struct typelist_end
    {
      typedef typelist_end
        type;
      typedef typelist_end
        next;
    };

    template<class type, class next = typelist_end>
    struct typelist_cons
    {
      typedef type
        type;
      typedef next
        next;

    private:
      // typelists must be proper!
      typedef typename next::type
        check_next_type;
      typedef typename next::next
        check_next_next;

      BOOST_STATIC_ASSERT((
        type_equal<typelist_end,next>::value
        ||
type_equal<typelist_cons<check_next_type,check_next_next>,next>::value));
    };

> > I'm interested in contributing to metaprogramming facilities of Boost.
>
> Good, let's cooperate then :)

I'll put some of my metaprogramming stuff into the Files section in the
near future.

> > Another problem is that endline layout breaks easily when
> > code is modified (e.g. identifiers renamed shorter or longer or
> > while adding typename or template keywords while bug-fixing).
> > Such changes can change the length of the line that controls the
> > indentation of the rest of the lines.
>
> That's true. That's the only problem that is real IMO. Still, I can live
> with such little burden in exchange to more pleasant/readable look of
the
> code.

As you wish. I previously used endline layout for some things, even
sometime after reading Code Complete. However, then I realized that
maintaining the layout is quite time consuming and not the kind of thing
that I would be proud of when I die. I much rather devote my time to
improving program logic than tinkering with the code layout. I still use
endline layout occasionally, but this is rather rare. You can get fully
accustomed to almost any kind of code layout in two weeks.

> > Endline layout refers to a layout convention in which
> > indentation is not,
> > in principle, invariant in respect to line length.
[snip]
> IMO "endline layout" of control structures is a different (from the
above)
> story.

In what way?

Thanks for the thoughts.


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