Boost logo

Boost :

From: Terje Slettebø (tslettebo_at_[hidden])
Date: 2004-09-29 14:46:48


>From: "Tobias Schwinger" <tschwinger_at_[hidden]>

> >>From: "Terje Slettebø"
> >
> > If we get a version of has_plus_op using this technique, we could
compare it
> > to the current version, in terms of dependencies, compilation speed
> > (instantiating it with a lot of types), etc.
>
> Hi again,
>
> got a modified version of your operator+ detection ready, which passes
> your test unit.
>
> http://tinyurl.com/6gtn2

I've now had a chance to look more at your version, and I'm impressed. :)
Great work. :) My hat off for what you've achieved in terms of drastic
reduction in compilation time and dependencies... You've even thrown in what
seems like a higher degree of compiler-portability. :)

What we're seeing is 1-2 _orders of magnitude_ reduction in compilation time
(and, I would also think, memory use), and nearly all external dependencies
gone. As you say, it should be possible to remove the rest, too (or change
them), should that be desirable.

John Torjo (who ran away screaming due to the compilation time): come back -
the library is now usable. ;) (At least, it will be, if we implement this
for it all)

I've dropped your has_plus_op version into the operator traits unit test,
and it compiles and runs flawlessly on Intel C++ 7.1, MSVC 7.1 and g++ 3.2.
As you said in an earlier posting: I guess I had to see it with my own eyes.
;)

Such an elegant way of specifying the "concept rules":

namespace has_plus_op_rules
{
  using namespace ::boost::type_filter::tagging_types;
  using namespace ::boost::type_filter::two_way;

  filt rule(any_type , any_type );
  pass rule(arithmetic_or_enum, arithmetic_or_enum);
  pass rule(obj_ptr_or_array , integral_or_enum );
  pass rule(integral_or_enum , obj_ptr_or_array );
  pass rule(aggregate , non_void );
  pass rule(non_void , aggregate );
  pass rule(aggregate , aggregate );

} // namespace has_plus_op_rules

I've been looking for something like this, but not found it, before now (for
some reason, the "rule"'s remind me of Boost.Spirit. ;) ).

Compare this to the original:

template<class T,class U = T>
struct has_plus_op :
  mpl::and_<
    mpl::or_<
      mpl::and_<
        detail::is_arithmetic_or_enum<typename remove_reference<T>::type>,
        detail::is_arithmetic_or_enum<typename remove_reference<U>::type>
>,
      mpl::and_<
        mpl::or_<
          is_array<typename remove_reference<T>::type>,
          detail::is_object_pointer<typename remove_reference<T>::type>
>,
        detail::is_integral_or_enum<typename remove_reference<U>::type>
>,
      mpl::and_<
        detail::is_integral_or_enum<typename remove_reference<T>::type>,
        mpl::or_<
          is_array<typename remove_reference<U>::type>,
          detail::is_object_pointer<typename remove_reference<U>::type>
>
>,
      detail::either_is_class_or_union_and_not_void<T,U>
>,
    detail::has_plus_op_impl<T,U>
> {};

Make no mistake: I like MPL (which should be evident from the above :) ),
but I can't help being reminded of how Kevlin Henney's latest version of
lexical_cast improved an earlier, experimental version, by using traits,
rather than brute-force switch-on-type. In the same way, the above is
remniscent of switch-on-type, compared to polymorphism, by using a
combination of overloading and traits not just for detection (as in the
original), but also for type categorisation. As you said in an earlier
posting, working with the type system, rather than brute-force testing.

You've certainly done a lot of work, here... The table still has a dent-mark
from my jaw. ;)

I see a lot of potential for this approach.

Good use on Boost configuration macros, as well (the original
operator/concept traits version was a little slim on that, but then again,
it was meant as a draft).

I also see you use inheritance to guide implicit conversions. Clever. :)

This also reminds me of concept inheritance (refinement):

  // Concrete object type representees
  struct aggregate
{ };
  struct array : obj_ptr_or_array
{ };
  struct enumeration : integral_or_enum , arithmetic_or_enum
{ };
  struct floating : arithmetic
{ };
  struct integral : arithmetic , integral_or_enum
{ };
  struct object_ptr : obj_ptr_or_array
{ };
  struct special_ptr : exceptional
{ };

It also goes further than the original version, checking for lvalue/rvalue,
which might also be useful for concept checking, where these are specified.

I'm wondering about the "two_way" part of the code, could you say what the
name alludes to, and what code that refers to? Also, regarding this part:

    type_filter::two_way::eq_pass<
      ::boost::has_plus_op_local::has_plus_op_prefilter<T,U>::value
>

Is "eq_pass" used, to enable other conditions, like "not_eq_pass", or
something like that? If not, why not something to this effect:

    mpl::bool_<
::boost::has_plus_op_local::has_plus_op_prefilter<T,U>::value==::boost::type
_filter::two_way::state_pass>

(The namespace nesting makes this look more complex than it is)

Or, if we let ::boost::has_plus_op_local::has_plus_op_prefilter<T,U> act as
mpl::bool_<>, itself (letting state_pass=true and state_filt=false):

::boost::has_plus_op_local::has_plus_op_prefilter<T,U>

> Nearly all dependecies are gone - I kept 'mpl::and_' (just because I was
> too lazy to change things which are likely to change again ;+) -
> actually we can get rid of this, too.
> ( For that matter it's probably a good idea to use these
> "bool_trait_(un)def" headers [boost/type_traits/detail], which seem much
> more portable than just inheriting from a "bool holder type").

That may be an idea. We could go through any remaining dependencies, and see
if we can do any reasonable changes to reduce their dependencies, again (and
possibly compilation time), and/or improve portability.

> I generated a simple stress test, instantiating it for about 500
> different types and the results look very promising:
>
> modified version | original version
> GCC 13 seconds | 5 minutes (*)
> MSVC 13 / 7(#) seconds | 1.5 minutes

Indeed they do... It's no wonder, either. After all, you've got rid of lots
of template instantiations.

> (*) the machine started swapping after gcc had ate up all
> physical memory
> (#) ANSI-mode /Za
>
> The categorization pipeline was successfully tested with
> gcc 3.2, 3.3 and 3.4, msvc 13.10.3077, bcc 5.5.1, 5.6.4.

That's great. :)

I'll contact you off-list for the way forward, but I'd be interested in
using this technique in all of the operator/concept traits library (where it
makes sense), or some other form of library for operator/concept checking.

Regards,

Terje


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