
From: "Tobias Schwinger" <tschwinger@neoscientists.org>
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.
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