Constrained genericity at last? (any interest in an operator traits/concept traits library?)

Colleagues: Are you tired of long and incomprehensible error messages when using STL, MPL, or some other modern template library? So am I! Up to now, we've had the Boost Concept Check Library, but unfortunately, you may still get long error messages with it (in the case of Intel C++ 6.0 and STLPort, pages and pages long, although the cause of the error is usually more clear, due to words like "concept_check", "function_requires", etc. showing up in the error message), and you can't overload on concepts, i.e. having several overloaded functions taking different kinds of iterators (such as std::advance). However, the invention of enable_if [HJW03] [JWL03] (now part of Boost) opened the door to overloading based on arbitrary properties of types. Now, what we needed was techniques for detecting type properties, which could be used to create "concept type traits". Using this, we could have done, e.g. for std::sort: template<class Iterator> typename enable_if<is_random_access_iterator<Iterator> >::type sort(Iterator begin, Iterator end) { // ... } Besides enable_if, there have also been other discoveries paving the way for constrained genericity (programming with concepts) in the later years. One of them being isAddable [KJSMA03], which could detect if there exists a "+" operator for the values "a" and "b" in "a + b", and gives a true/false result. The operator could be built-in, member function, or free function; it didn't matter. Other work on type property introspection in C++ has been done by other people, notably Paul Mensonides, with techniques for detecting member types, (non-static) member variables and functions, static member variables and functions, etc., using SFINAE to great effect. The original isAddable required exact overload match - no conversion on the arguments was allowed, so int + int gave true, but int + double gave false, even though 1 + 1.0 is well-formed - but using a modified version, it's possible to allow the normal promotions/conversions on the arguments, as well, letting int + double give true. In contrast, the other mentioned detection techniques requires an exact signature match, so these two ways complement each other: The exact match way may be used where the promotions/conversions way can't be used, and vice versa. Last spring (after the introduction of enable_if and isAddable) I realised that we now for the first time had discovered or invented all the components needed for a decent implementation of constrained genericity (or programming with concepts) in the current C++ language (as shown with the code example above)! I therefore started on a library of type traits for detecting the components required for concept checking, which meant detecting such things as operators, constructors (to the degree it's possible), etc. The modified isAddable is a key technique used in this "operator traits" part of the library. When this was complete, I continued with the "concept traits" part of the library, where the aim was to make type traits for all the C++ standard concepts, as well as all the Boost.MPL concepts (for the reasons that MPL is widely known and used, it's part of Boost, it could help a lot with better error messages in such metaprogramming libraries, and as an example of implementing non C++ standard concepts). I was about halfway done with the library last summer, and then I got a new job, where I became very busy, so unfortunately I couldn't continue with it at that time. However, a while ago, I've resumed work on the library, and it's now complete. Besides the mentioned enable_if, isAddable, and the introspection techniques, MPL is a core component of the library. Its lazy evaluation logical operators are indispensible for many of the concept traits, particularly the more complex ones. I couldn't have done it without any of you. Thank you. MPL is also used in the unit tests, generating the test matrix on the fly, testing each trait in a list against a list of types, and comparing the result against a table. The total number of tests generated is several thousand. Other approaches ------------------------- About a year ago, there have been proposals/discussion papers for adding support for concepts to the C++ language (see N1510, N1522 and N1536 at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/). Using this, the above example could have been written as: template<RandomAccessIterator Iterator> void sort(Iterator begin, Iterator end) { // ... } This would also ease implementation of such concept overloading, because it would include the concept of concept refinement and "best match". Using the enable_if solution is a match or no match situation: either a function or class template matches, or it doesn't. Thus, if you want to implement std::advance as several overloaded functions using enable_if, you have to do something like this: template<class Iterator,class Distance> typename enable_if< mpl::and_< mpl::or_< is_input_iterator<Iterator>, is_output_iterator<Iterator> >, mpl::not_< is_bidirectional_iterator<Iterator> >
::type
advance(Iterator i,Distance d) { while(d--!=0) ++i; } The above function template is called if Iterator is either an InputIterator or OutputIterator, but not BidirectionalIterator (or "higher"). I.e. you have to manually exclude the next higher concept in the hierarchy, since BidirectionalIterator is also an InputIterator and OutputIterator (but should then call another overloaded function, not this one). If you also have a std::advance overload for BidirectionalIterator, you would otherwise get an error about ambiguity for the overloads (as they both would match), when called with a BidirectionalIterator. Nevertheless, the operator traits/concept traits library is something that can be used _now_, and which may give us more experience with programming with concepts. If the traits are used with libraries like STL or MPL, it has the potential to give drastically better diagnostics, and thereby making them easier to use. In addition, overloading on concepts is possible. A few of the traits (is_assignable, is_default_constructible and is_copy_constructible) rely on BCCL as a "last resort" when used for user-defined types, as there is no known way of detecting these for user-defined types. The library, including documentation, examples and tests may be found at (http://home.broadpark.no/~terjesl/operator_concept_traits.zip) (Yahoo Files is full, could we perhaps remove some of the stuff there that is now part of Boost?). Here's the synopsis from the documentation: Synopsis ------------- The library contains: - 46 operator type traits, covering all the overloadable (and therefore detectable) operators, as well as detection of default constructor, copy constructor and assignment operator. - 37 C++ standard concepts type traits, covering all the concepts of the C++ standard. - 19 Boost.MPL concepts type traits, covering all the documented concepts of Boost.MPL. - Facilities for defining your own concept type traits. - A complete set of unit tests. - Example programs, demonstrating its use. ------------- Some components may duplicate existing parts of Boost (such as MPL's has_xxx.hpp for member type detection, and mpl::is_sequence), and in other cases, the library includes functionality that may better belong in existing Boost libraries (type traits and MPL), and in those known cases, it's noted in the documentation. An aim is of course to syncronise the libraries, so that duplication is avoided. There may also be other components in the library that duplicate parts of MPL (or other parts of Boost) not noted here, and I'm all ears hearing about that, so that duplication may be eliminated. The operator traits/concept traits is essentially an extension to the Boost type traits library, but due to its specialisation (detecting operators, and generic concepts), as well as its size, it may be better to have as one or more separate components. Compatibility The library has been tested on Intel C++ 7.1, MSVC 7.1 and g++ 3.2. All tests pass on Intel C++ 7.1 and MSVC 7.1. For g++ 3.2, the operator traits tests pass, but most of the concept traits fails (either at compile time, or give wrong result). I intend to work to make the library work for g++ 3.2, as well. Bibliography [HJW03] Howard Hinnant, Jaakko Järvi and Jeremiah Willcock, Function Overloading Based on Arbitrary Properties of Types (http://www.cuj.com/articles/2003/0306/), 2003 [JWL03] Jaakko Järvi, Jeremiah Willcock, Andrew Lumsdaine, boost::enable_if, 2003 [KJSMA03] Dietmar Kuehl, Jaakko Jarvi, Jeremy Siek, Mat Marcus and Dave Abrahams, isAddable (ACCU Conference 2003 slides) (Regarding the salutation, I couldn't help doing a "Mat Marcus" here (http://article.gmane.org/gmane.comp.lib.boost.devel/42877), ;) not at least since Mat is also one of the people behind isAddable, a key technique of this library) Regards, Terje P.S. For those who may be wondering where I've been all this time, the reason was the mentioned new job, which lead to me being away from more or less the C++ community for about a year, until a few months ago. P.P.S. I've also seen the last days there have been discussion on the Boost list on similar facilities (the "Proposal for 'is_dereferenceable' and other templatemetafunctions" thread started by Alex Chovanec). is_dereferenceable<T> is similar to this library's has_dereference_op<T> (in the operator traits part), except that the latter doesn't test for lvalue, etc. Moreover, some of the other existing functionality may work better than the corresponding ones in this library (like MPL's has_xxx.hpp, which, from the comments in the header file is able to detect reference member types, something this library can't), so maybe we could cooperate? P.P.P.S From libraries in the Boost Sandbox, it appears that it's ok, even common, for libraries that are not part of Boost to still use the Boost logo in the documentation. Therefore, that is done in this case, as well. I also hope John Maddock doesn't mind that I used the same kind of tables for these traits, and general style, as for the Boost type traits library. Feedback on this library is very welcome.

Colleagues:
When this was complete, I continued with the "concept traits" part of the library, where the aim was to make type traits for all the C++ standard concepts, as well as all the Boost.MPL concepts (for the reasons that MPL is widely known and used, it's part of Boost, it could help a lot with better error messages in such metaprogramming libraries, and as an example of implementing non C++ standard concepts).
You might be interested in a library I wrote during the winter called 'Metafunction Traits.' Quoting the docs: "Metafunction Traits provides three unary boolean-valued MPL Metafunctions for determining whether an arbitrary type is an MPL lambda expression or model of the concept Metafunction Class. This allows metafunction classes and lambda expressions to be used polymorphically with non-metaprogramming types." The library is here: http://tinyurl.com/43ddj. I didn't posted it because I never finished writing the discussion section. The library works on these compilers: * Microsoft Visual C++ 7.1 * Metrowerks CodeWarrior 9.2 * GCC 3.2 (MinGW) * GCC 3.3.1 (cygming special) * Intel C++ Compiler for Windows 7.1 and 8.0 * Comeau C/C++ 4.3.3 * Borland C++ 6.0 (preview) It needs to be updated to reflect the new definition of nullary metafunction class. And I should probably reimplement it to use 'local iteration', now that I know what that is ;-) BTW, I left out the notion of the arity of a metafunction or lambda expression for two reasons: 1. The version which was easy to implement had slightly different semantics for lambda expressions and metafunction classes 2. Metafunction classes can be polyadic or have undocumented trailing parameters, so the notion of the exact arity of a metafunction class is not really that useful Best Regards, Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:ci5qf6$ioc$1@sea.gmane.org...
The library is here: http://tinyurl.com/43ddj.
That's the link for the docs. The download page is here: http://tinyurl.com/4axzk. Best Regards, Jonathan Turkanis

"Jonathan Turkanis" <technews@kangaroologic.com> writes:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:ci5qf6$ioc$1@sea.gmane.org...
The library is here: http://tinyurl.com/43ddj.
That's the link for the docs. The download page is here: http://tinyurl.com/4axzk.
Surely this functionality already exists somewhere in the MPL? Yup, there's is_lambda_expression<E>::value (which is true iff is_placeholder_expression<E>::value | is_metafunction_class<E>::value). -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:upt4pvvfy.fsf@boost-consulting.com...
"Jonathan Turkanis" <technews@kangaroologic.com> writes:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:ci5qf6$ioc$1@sea.gmane.org...
The library is here: http://tinyurl.com/43ddj.
That's the link for the docs. The download page is here: http://tinyurl.com/4axzk.
Surely this functionality already exists somewhere in the MPL? Yup, there's is_lambda_expression<E>::value (which is true iff is_placeholder_expression<E>::value | is_metafunction_class<E>::value).
I wrote this last winter. is_lambda_expression seems to be a recent (very welcome) addition. When I searched throught the mpl source for is_lambda_expression, is_metafunction_class, is_metafunction, etc. all I found was the nested type is_le. I tried to define is_lambda_expression like this template<typename T> struct is_lambda_expression { typedef typename lambda<T>::is_le type; static const bool value = type::value; }; and it almost worked, but failed for some metafunctions, including always<int>. Anyway, I'm glad to see it's there now. Perhaps the macro BOOST_HAS_TEMPLATE_XXX_TRAIT_NAMED_DEF would still be useful? Jonathan

The library, including documentation, examples and tests may be found at (http://home.broadpark.no/~terjesl/operator_concept_traits.zip) (Yahoo Files is full, could we perhaps remove some of the stuff there that is now part of Boost?).
For interest, count me in ;) I'll take a look when I have the time - which might be kind of 3-4 weeks from now :( Anyway, when I'm particularly interested in, are compile-time speeds. Best, John -- John Torjo -- john@torjo.com Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -- v1.4.0 - save_dlg - true binding of your data to UI controls! + easily add validation rules (win32gui/examples/smart_dlg)

The library, including documentation, examples and tests may be found at (http://home.broadpark.no/~terjesl/operator_concept_traits.zip) (Yahoo Files is full, could we perhaps remove some of the stuff there that is now part of Boost?).
For interest, count me in ;)
Glad to hear it. As you may have seen from this thread (and the "is_dereferenceable" thread), there also exists several approaches to this. Maybe a "super-implementation" can result from it all. :)
Anyway, when I'm particularly interested in, are compile-time speeds.
What's your next question? ;) Ok, I haven't timed "typical use"; the only tests I've done in this respect is running the unit tests, but much of the time involved in that may well be taken by the test framework, as it uses mpl::for_each to iterate in two dimensions, taking a list of traits, and a list of types to test for each trait, which generates thousands of tests. The unit tests take a rather long time to compile on Intel C++ (typically several minutes), but shorter on on MSVC, or g++. Regards, Terje

Ok, I haven't timed "typical use"; the only tests I've done in this respect is running the unit tests, but much of the time involved in that may well be taken by the test framework, as it uses mpl::for_each to iterate in two dimensions, taking a list of traits, and a list of types to test for each trait, which generates thousands of tests.
The unit tests take a rather long time to compile on Intel C++ (typically several minutes), but shorter on on MSVC, or g++.
that's a bit of a turn-off for me. I'm getting more and more annoyied by the fact that every time something *really cool* is discovered, it hits the compiler-time barrier. As I've said in posts to c.l.c++.m, maybe it's time for a new language... Best, John -- John Torjo -- john@torjo.com Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -- v1.4.0 - save_dlg - true binding of your data to UI controls! + easily add validation rules (win32gui/examples/smart_dlg)

From: "John Torjo" <john.lists@torjo.com>
Ok, I haven't timed "typical use"; the only tests I've done in this respect is running the unit tests, but much of the time involved in that may well be taken by the test framework, as it uses mpl::for_each to iterate in two dimensions, taking a list of traits, and a list of types to test for each trait, which generates thousands of tests.
The unit tests take a rather long time to compile on Intel C++ (typically several minutes), but shorter on on MSVC, or g++.
that's a bit of a turn-off for me. I'm getting more and more annoyied by the fact that every time something *really cool* is discovered, it hits the compiler-time barrier.
As I've said in posts to c.l.c++.m, maybe it's time for a new language...
Or compilers optimised for heavy template use. :) There might be ways of optimising compilation time, but it may be very compiler-specific (http://users.rcn.com/abrahams/instantiation_speed/index.html). I've done some benchmarks, using the unit tests. The results are given below. System specification ----------------------------- Pentium II 350 MHz 384 MB 100 MHz RAM operator_traits_test.cpp --------------------------------- Intel C++ 7.1 - No debug info, optimisations off ---------------------------------------------------------------- 21 traits, 925 tests - 5 min 46 traits, 1981 tests - 21 min MSVC 7.1 - No debug info, optimisations off ------------------------------------------------------------- 21 traits, 925 tests - 2 min 46 traits, 1981 tests - N/A - fatal error C1204: compiler limit : internal structure overflow g++ 3.2 - No debug info, optimisations off --------------------------------------------------------- 21 traits, 925 tests - 7 min 46 traits, 1981 tests - > 30 min (*) (*) This used a lot of memory, > 330 MB, apparently leading to virtual memory trashing, and dramatic drop in processor utilisation, so in the end I stopped the compilation. Note that, as mentioned above, much of the overhead may be due to the test setup - it builds a long instantiation stack with mpl::for_each in two dimensions, and as you can see, on Intel C++ (EDG), the time taken doesn't grow linearly with the number of tests, but rather quadratic, or something like it. The traits are rather simple in structure - there's no recursion in the library itself, although it might be in the mpl::and_<>, mpl::or_<> and mpl::not_<> components that are used, and/or use of the PP-lib. Before doing any optimisation, I think one should first determine if there _is_ a problem, by benchmarking a real-life program with and without concept checking. Regards, Terje

Terje Slettebø <tslettebo@broadpark.no> writes:
Colleagues:
Are you tired of long and incomprehensible error messages when using STL, MPL, or some other modern template library? So am I!
Up to now, we've had the Boost Concept Check Library, but unfortunately, you may still get long error messages with it (in the case of Intel C++ 6.0 and STLPort, pages and pages long, although the cause of the error is usually more clear, due to words like "concept_check", "function_requires", etc. showing up in the error message), and you can't overload on concepts, i.e. having several overloaded functions taking different kinds of iterators (such as std::advance).
However, the invention of enable_if [HJW03] [JWL03] (now part of Boost) opened the door to overloading based on arbitrary properties of types. Now, what we needed was techniques for detecting type properties, which could be used to create "concept type traits". Using this, we could have done, e.g. for std::sort:
template<class Iterator> typename enable_if<is_random_access_iterator<Iterator> >::type sort(Iterator begin, Iterator end) { // ... }
enable_if doesn't always improve error messages. Sometimes you get a long list of candidate functions when what you really wanted was a message from inside just one of the overloads that indicates how concept conformance was violated.
Besides enable_if, there have also been other discoveries paving the way for constrained genericity (programming with concepts) in the later years. One of them being isAddable [KJSMA03], which could detect if there exists a "+" operator for the values "a" and "b" in "a + b", and gives a true/false result. The operator could be built-in, member function, or free function; it didn't matter.
Other work on type property introspection in C++ has been done by other people, notably Paul Mensonides, with techniques for detecting member types, (non-static) member variables and functions, static member variables and functions, etc., using SFINAE to great effect.
The original isAddable required exact overload match - no conversion on the arguments was allowed, so int + int gave true, but int + double gave false, even though 1 + 1.0 is well-formed - but using a modified version, it's possible to allow the normal promotions/conversions on the arguments, as well, letting int + double give true.
In contrast, the other mentioned detection techniques requires an exact signature match, so these two ways complement each other: The exact match way may be used where the promotions/conversions way can't be used, and vice versa.
Last spring (after the introduction of enable_if and isAddable) I realised that we now for the first time had discovered or invented all the components needed for a decent implementation of constrained genericity (or programming with concepts) in the current C++ language (as shown with the code example above)!
I therefore started on a library of type traits for detecting the components required for concept checking, which meant detecting such things as operators, constructors (to the degree it's possible), etc. The modified isAddable is a key technique used in this "operator traits" part of the library.
Isn't that what we've already been discussing in http://news.gmane.org/find-root.php?message_id=%3cchr3k4%241b1%241%40sea.gma... (http://tinyurl.com/4fx3u) ?
When this was complete, I continued with the "concept traits" part of the library, where the aim was to make type traits for all the C++ standard concepts, as well as all the Boost.MPL concepts (for the reasons that MPL is widely known and used, it's part of Boost, it could help a lot with better error messages in such metaprogramming libraries, and as an example of implementing non C++ standard concepts).
I was about halfway done with the library last summer, and then I got a new job, where I became very busy, so unfortunately I couldn't continue with it at that time. However, a while ago, I've resumed work on the library, and it's now complete.
Besides the mentioned enable_if, isAddable, and the introspection techniques, MPL is a core component of the library. Its lazy evaluation logical operators are indispensible for many of the concept traits, particularly the more complex ones. I couldn't have done it without any of you. Thank you.
MPL is also used in the unit tests, generating the test matrix on the fly, testing each trait in a list against a list of types, and comparing the result against a table. The total number of tests generated is several thousand.
Other approaches -------------------------
About a year ago, there have been proposals/discussion papers for adding support for concepts to the C++ language (see N1510, N1522 and N1536 at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/). Using this, the above example could have been written as:
<snip real concepts> Real concepts have the advantage of being able to dispatch to the "most-refined" overload.
Nevertheless, the operator traits/concept traits library is something that can be used _now_, and which may give us more experience with programming with concepts. If the traits are used with libraries like STL or MPL, it has the potential to give drastically better diagnostics, and thereby making them easier to use. In addition, overloading on concepts is possible.
A few of the traits (is_assignable, is_default_constructible and is_copy_constructible) rely on BCCL as a "last resort" when used for user-defined types, as there is no known way of detecting these for user-defined types.
The library, including documentation, examples and tests may be found at (http://home.broadpark.no/~terjesl/operator_concept_traits.zip) (Yahoo Files is full, could we perhaps remove some of the stuff there that is now part of Boost?).
Here's the synopsis from the documentation:
Synopsis ------------- The library contains:
- 46 operator type traits, covering all the overloadable (and therefore detectable) operators, as well as detection of default constructor, copy constructor and assignment operator.
- 37 C++ standard concepts type traits, covering all the concepts of the C++ standard.
- 19 Boost.MPL concepts type traits, covering all the documented concepts of Boost.MPL.
Things have changed quite a bit since the last release of MPL. Are you sure you're up-to-date?
- Facilities for defining your own concept type traits.
- A complete set of unit tests.
- Example programs, demonstrating its use. -------------
Some components may duplicate existing parts of Boost (such as MPL's has_xxx.hpp for member type detection, and mpl::is_sequence), and in other cases, the library includes functionality that may better belong in existing Boost libraries (type traits and MPL), and in those known cases, it's noted in the documentation. An aim is of course to syncronise the libraries, so that duplication is avoided.
There may also be other components in the library that duplicate parts of MPL (or other parts of Boost) not noted here, and I'm all ears hearing about that, so that duplication may be eliminated.
The operator traits/concept traits is essentially an extension to the Boost type traits library, but due to its specialisation (detecting operators, and generic concepts), as well as its size, it may be better to have as one or more separate components.
Compatibility
The library has been tested on Intel C++ 7.1, MSVC 7.1 and g++ 3.2. All tests pass on Intel C++ 7.1 and MSVC 7.1. For g++ 3.2, the operator traits tests pass, but most of the concept traits fails (either at compile time, or give wrong result). I intend to work to make the library work for g++ 3.2, as well.
Bibliography
[HJW03] Howard Hinnant, Jaakko Järvi and Jeremiah Willcock, Function Overloading Based on Arbitrary Properties of Types (http://www.cuj.com/articles/2003/0306/), 2003 [JWL03] Jaakko Järvi, Jeremiah Willcock, Andrew Lumsdaine, boost::enable_if, 2003 [KJSMA03] Dietmar Kuehl, Jaakko Jarvi, Jeremy Siek, Mat Marcus and Dave Abrahams, isAddable (ACCU Conference 2003 slides)
(Regarding the salutation, I couldn't help doing a "Mat Marcus" here (http://article.gmane.org/gmane.comp.lib.boost.devel/42877), ;) not at least since Mat is also one of the people behind isAddable, a key technique of this library)
Regards,
Terje
P.S. For those who may be wondering where I've been all this time, the reason was the mentioned new job, which lead to me being away from more or less the C++ community for about a year, until a few months ago.
P.P.S. I've also seen the last days there have been discussion on the Boost list on similar facilities (the "Proposal for 'is_dereferenceable' and other templatemetafunctions" thread started by Alex Chovanec). is_dereferenceable<T> is similar to this library's has_dereference_op<T> (in the operator traits part), except that the latter doesn't test for lvalue, etc.
Moreover, some of the other existing functionality may work better than the corresponding ones in this library (like MPL's has_xxx.hpp, which, from the comments in the header file is able to detect reference member types, something this library can't), so maybe we could cooperate?
P.P.P.S From libraries in the Boost Sandbox, it appears that it's ok, even common, for libraries that are not part of Boost to still use the Boost logo in the documentation. Therefore, that is done in this case, as well. I also hope John Maddock doesn't mind that I used the same kind of tables for these traits, and general style, as for the Boost type traits library.
Feedback on this library is very welcome.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Terje Slettebø <tslettebo@broadpark.no> writes:
Colleagues:
Are you tired of long and incomprehensible error messages when using STL, MPL, or some other modern template library? So am I!
Up to now, we've had the Boost Concept Check Library, but unfortunately, you may still get long error messages with it (in the case of Intel C++ 6.0 and STLPort, pages and pages long, although the cause of the error is usually more clear, due to words like "concept_check", "function_requires", etc. showing up in the error message), and you can't overload on concepts, i.e. having several overloaded functions taking different kinds of iterators (such as std::advance).
However, the invention of enable_if [HJW03] [JWL03] (now part of Boost) opened the door to overloading based on arbitrary properties of types. Now, what we needed was techniques for detecting type properties, which could be used to create "concept type traits". Using this, we could have done, e.g. for std::sort:
template<class Iterator> typename enable_if<is_random_access_iterator<Iterator> >::type sort(Iterator begin, Iterator end) { // ... }
enable_if doesn't always improve error messages. Sometimes you get a long list of candidate functions when what you really wanted was a message from inside just one of the overloads that indicates how concept conformance was violated.
Besides enable_if, there have also been other discoveries paving the way for constrained genericity (programming with concepts) in the later years. One of them being isAddable [KJSMA03], which could detect if there exists a "+" operator for the values "a" and "b" in "a + b", and gives a true/false result. The operator could be built-in, member function, or free function; it didn't matter.
Other work on type property introspection in C++ has been done by other people, notably Paul Mensonides, with techniques for detecting member types, (non-static) member variables and functions, static member variables and functions, etc., using SFINAE to great effect.
The original isAddable required exact overload match - no conversion on the arguments was allowed, so int + int gave true, but int + double gave false, even though 1 + 1.0 is well-formed - but using a modified version, it's possible to allow the normal promotions/conversions on the arguments, as well, letting int + double give true.
In contrast, the other mentioned detection techniques requires an exact signature match, so these two ways complement each other: The exact match way may be used where the promotions/conversions way can't be used, and vice versa.
Last spring (after the introduction of enable_if and isAddable) I realised that we now for the first time had discovered or invented all the components needed for a decent implementation of constrained genericity (or programming with concepts) in the current C++ language (as shown with the code example above)!
I therefore started on a library of type traits for detecting the components required for concept checking, which meant detecting such things as operators, constructors (to the degree it's possible), etc. The modified isAddable is a key technique used in this "operator traits" part of the library.
Isn't that what we've already been discussing in http://news.gmane.org/find-root.php?message_id=%3cchr3k4%241b1%241%40sea.gma... (http://tinyurl.com/4fx3u) ?
When this was complete, I continued with the "concept traits" part of the library, where the aim was to make type traits for all the C++ standard concepts, as well as all the Boost.MPL concepts (for the reasons that MPL is widely known and used, it's part of Boost, it could help a lot with better error messages in such metaprogramming libraries, and as an example of implementing non C++ standard concepts).
I was about halfway done with the library last summer, and then I got a new job, where I became very busy, so unfortunately I couldn't continue with it at that time. However, a while ago, I've resumed work on the library, and it's now complete.
Besides the mentioned enable_if, isAddable, and the introspection techniques, MPL is a core component of the library. Its lazy evaluation logical operators are indispensible for many of the concept traits, particularly the more complex ones. I couldn't have done it without any of you. Thank you.
MPL is also used in the unit tests, generating the test matrix on the fly, testing each trait in a list against a list of types, and comparing the result against a table. The total number of tests generated is several thousand.
Other approaches -------------------------
About a year ago, there have been proposals/discussion papers for adding support for concepts to the C++ language (see N1510, N1522 and N1536 at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/). Using this, the above example could have been written as:
<snip real concepts> Real concepts have the advantage of being able to dispatch to the "most-refined" overload.
Nevertheless, the operator traits/concept traits library is something that can be used _now_, and which may give us more experience with programming with concepts. If the traits are used with libraries like STL or MPL, it has the potential to give drastically better diagnostics, and thereby making them easier to use. In addition, overloading on concepts is possible.
A few of the traits (is_assignable, is_default_constructible and is_copy_constructible) rely on BCCL as a "last resort" when used for user-defined types, as there is no known way of detecting these for user-defined types.
The library, including documentation, examples and tests may be found at (http://home.broadpark.no/~terjesl/operator_concept_traits.zip) (Yahoo Files is full, could we perhaps remove some of the stuff there that is now part of Boost?).
Here's the synopsis from the documentation:
Synopsis ------------- The library contains:
- 46 operator type traits, covering all the overloadable (and therefore detectable) operators, as well as detection of default constructor, copy constructor and assignment operator.
- 37 C++ standard concepts type traits, covering all the concepts of the C++ standard.
- 19 Boost.MPL concepts type traits, covering all the documented concepts of Boost.MPL.
Things have changed quite a bit since the last release of MPL. Are you sure you're up-to-date?
P.P.S. I've also seen the last days there have been discussion on the Boost list on similar facilities (the "Proposal for 'is_dereferenceable' and other templatemetafunctions" thread started by Alex Chovanec). is_dereferenceable<T> is similar to this library's has_dereference_op<T> (in the operator traits part), except that the latter doesn't test for lvalue, etc.
I think the big advantage of Daniel Wallin's last crack at that is that it provides the means to *generate* new metafunctions for detecting namespace-scope-overloadable functions (including operators) via a macro. I think providing the automation is probably more important than providing a sweeping catalog of individual metafunctions, though I could be wrong. I think I'd rather see the concept detection metafunctions in the libraries that define those concepts, rather than concentrated in one place... ... this is a similar problem to the problem of adding stream inserters and extractors, in terms of dependencies, except that most of the libraries use the MPL in one way or another already. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

From: "David Abrahams" <dave@boost-consulting.com>
Terje Slettebø <tslettebo@broadpark.no> writes:
Are you tired of long and incomprehensible error messages when using STL, MPL, or some other modern template library? So am I!
template<class Iterator> typename enable_if<is_random_access_iterator<Iterator> >::type sort(Iterator begin, Iterator end) { // ... }
enable_if doesn't always improve error messages. Sometimes you get a long list of candidate functions when what you really wanted was a message from inside just one of the overloads that indicates how concept conformance was violated.
How can you get a list of candidate functions, if enable_if excludes them from the overload set? Could you give a code example?
Isn't that what we've already been discussing in
http://news.gmane.org/find-root.php?message_id=%3cchr3k4%241b1%241%40sea.gma...
Other approaches -------------------------
About a year ago, there have been proposals/discussion papers for adding support for concepts to the C++ language (see N1510, N1522 and N1536 at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/). Using this,
Yes, and I noted that thread at the end of the posting. However, at least at the beginning of that thread, only one property was detected, dereferenceability. I see that it has later been generalised to any overloadable operator. I'll come to Daniel Wallin's approach below here (where you mention it), but I've had a look at Alex Chovanec's is_dereferenceable in Yahoo Files, and it contains some clever tricks, such as overloading the comma operator to permit detection of overloaded operators returning void. Definitely one I'd like to add. There are also similar facilites as BOOST_IS_XXXABLE_PREFIX()/BOOST_IS_XXXABLE_POSTFIX() in the operator/concept traits library, although they are not documented, as such detection for all the overloadable operators is already defined in the library, so the user shouldn't really have a need for it. Nevertheless, they are found in the operator_traits/detail/unop.hpp (prefix), unop2.hpp (postfix) and binop.hpp headers. These headers contain macros such as BOOST_OT_DETAIL_DEFINE_UNOP_TRAIT(name,op), which again define templates for detecting the given operator. For example BOOST_OT_DETAIL_DEFINE_UNOP_TRAIT(has_dereference_op, *) will generate the template has_dereference_op<T>, which will detect the presence of the dereference operator for T. The current version of is_xxxable.hpp in Yahoo Files does seem to have some problems, when it comes to implicit conversions. Unfortunately, I found that the same problem exists in the operator traits (has_dereference_op). :) The following program gives a compiler error for is_dereferenceable: #include <iostream> #include "is_dereferenceable.hpp" // Uses is_xxxable.hpp struct A {}; struct B { operator A() { return A(); } }; A operator*(A) { return A(); } using namespace boost::detail; int main() { std::cout << is_dereferenceable<A>::value << "\n"; // Ok std::cout << is_dereferenceable<B>::value << "\n"; // Error, ambiguity between A and any_conversion_eater A a; B b; *a; // Sure *b; // No problem, either; B is implicitly converted to A, which does have a dereference operator } Also, it didn't have a binary operator detection, or I'd liked to test if both int + int and int + double (requires promotion) would work (it does with the operator traits). the
above example could have been written as:
<snip real concepts>
Real concepts have the advantage of being able to dispatch to the "most-refined" overload.
Yes, that's what I said just below the part you quote above:
This would also ease implementation of such concept overloading, because it would include the concept of concept refinement and "best match".
Synopsis ------------- The library contains:
- 46 operator type traits, covering all the overloadable (and therefore detectable) operators, as well as detection of default constructor, copy constructor and assignment operator.
- 37 C++ standard concepts type traits, covering all the concepts of the C++ standard.
- 19 Boost.MPL concepts type traits, covering all the documented concepts of Boost.MPL.
Things have changed quite a bit since the last release of MPL. Are you sure you're up-to-date?
That's a little hard to tell, given that the docs haven't been updated, yet (at least for the trunk). I've covered what is documented, including the release notes for the coming Boost 1.32 release, that Aleksey posted a while ago.
P.P.S. I've also seen the last days there have been discussion on the Boost list on similar facilities (the "Proposal for 'is_dereferenceable' and other templatemetafunctions" thread started by Alex Chovanec). is_dereferenceable<T> is similar to this library's has_dereference_op<T> (in the operator traits part), except that the latter doesn't test for lvalue, etc.
I think the big advantage of Daniel Wallin's last crack at that is that it provides the means to *generate* new metafunctions for detecting namespace-scope-overloadable functions (including operators) via a macro.
I agree that it's a good approach, and as mentioned above, a similar one is used in the implementation of the operator/concept traits, and which is also available to users.of the library (they are described in the "type_traits_ext" part of the docs). The traits for the C++ standard and MPL concepts is simply provided for "convenience" (and "proof of concept", nu pun intended. :) ), saving people from the hard work of defining them all. This is completely analoguous to the way BCCL provides components for defining your own concept checks, while also providing concept checks for most of the C++ standard concepts. Unfortunately, I was unable to get Daniel Wallin's result_check_test.cpp to compile, as I got the following error on Intel C++ (latest Boost Sandbox was used): boost-sandbox\libs\utility\test\result_check_test.cpp(8): error: identifier "void_result_tag" is undefined BOOST_RESULT_CHECK(2, add, operator+, _1 + _2) ^
I think providing the automation is probably more important than providing a sweeping catalog of individual metafunctions, though I could be wrong.
I think you're right. BOOST_OT_DEFINE_UNOP_TRAIT(name, op) demonstrates this above (just like Alex and Daniel's approach). To show a simple example of how to build on this, again (using existing traits): Say you have a concept which requires the types involved to be Assignable and have the "+" and "-" operators defined for them (including allowing implicit promotions/conversions). Here's the concept trait: #include <boost/operator_traits/has_plus_op.hpp> #include <boost/operator_traits/has_minus_op.hpp> #include <boost/concept_traits/std/is_assignable.hpp> template<class T1,class T2> struct is_my_concept : mpl::and_< is_assignable<T1>, is_assignable<T2>, has_plus_op<T1, T2>, has_minus_op<T1, T2>
{};
and you're done. Note that you may do "concept refinement" by simply including the concept you refine in the and_<>-list.
I think I'd rather see the concept detection metafunctions in the libraries that define those concepts, rather than concentrated in one place...
That's an idea, and as shown above, you can define your own traits. However, if you already have a library that can detect all the operators, as well as facilities for detecting member functions, etc., your own implementation can be rather light-weight. Alternatively, you might go from "first principles" and define the is_assignable, has_plus_op, and has_minus_op traits, yourself. However, they are non-trivial to define, as you have to "filter" the types you test for, to avoid compilation errors (as Daniel mentioned in a posting, about specialising for fundamental types, pointers, etc.). Regards, Terje

Terje Slettebø <tslettebo@broadpark.no> writes:
There are also similar facilites as BOOST_IS_XXXABLE_PREFIX()/BOOST_IS_XXXABLE_POSTFIX() in the operator/concept traits library, although they are not documented, as such detection for all the overloadable operators is already defined in the library, so the user shouldn't really have a need for it. Nevertheless, they are found in the operator_traits/detail/unop.hpp (prefix), unop2.hpp (postfix) and binop.hpp headers.
The current version of is_xxxable.hpp in Yahoo Files does seem to have some problems, when it comes to implicit conversions.
I discovered another problem with these macros after I uploaded them. The original 'is_dereferenceable' implementation relied upon special handling for fundamental types. The reason is that attempting to dereference something like an 'int' or 'float' is going to cause a compiler error no matter what, even if there is a last resort 'operator*'. To solve this problem, I rigged 'is_dereferenceable' to return false for all fundamental types. (Pointers are not considered fundamental types.) This special handling was carried over into the 'is_xxxable' macros, and it doesn't generalize to other operators. For instance, if I use 'BOOST_IS_XXXABLE_PREFIX' to generate 'is_incrementable', I get a metafunction that returns 'false' for integers. Of course, this problem can be fixed, but probably not without some special handling for specific fundamental types and/or operators, which kind of defeats the purpose of having a generalized macro. -- Alex Chovanec NIF and Engineering Division Computing Applications and Research Department Lawrence Livermore National Laboratory

From: "Alex Chovanec" <chovanec@llnl.gov>
Terje Slettebø <tslettebo@broadpark.no> writes:
The current version of is_xxxable.hpp in Yahoo Files does seem to have some problems, when it comes to implicit conversions.
I discovered another problem with these macros after I uploaded them. The original 'is_dereferenceable' implementation relied upon special handling for fundamental types. The reason is that attempting to dereference something like an 'int' or 'float' is going to cause a compiler error no matter what, even if there is a last resort 'operator*'. To solve this problem, I rigged 'is_dereferenceable' to return false for all fundamental types. (Pointers are not considered fundamental types.) This special handling was carried over into the 'is_xxxable' macros, and it doesn't generalize to other operators. For instance, if I use 'BOOST_IS_XXXABLE_PREFIX' to generate 'is_incrementable', I get a metafunction that returns 'false' for integers.
Of course, this problem can be fixed, but probably not without some special handling for specific fundamental types and/or operators, which kind of defeats the purpose of having a generalized macro.
Exactly. If you look at the implementation of the traits in the library I uploaded, e.g. has_plus_op, you'll see that it has a two-part implementation: The first part "filters" the types (using type traits and MPL's logical metafunctions, not specialisations), and it ends with "detail::has_plus_op_impl<T,U>". Only if it passes the first part, will it attempt this final check, where it tries to perform T1 + T2 (with the ...-overload if there's no existing operator+ for them). The first filtering ensures that - as you say about dereferencing the fundamental types - it doesn't try to do e.g. void + void, because that will fail with an error, regardless of the ...-overload (overloaded functions are not considered for built-in types). On the surface, it may seem logical that it should be possible to write a "generic macro" for all operators, but when you get down to actually doing it, and testing it against all kinds of types, you find that you need this "pre-filtering" phase to avoid errors, and as you also point out, that phase is different for different operators. It's not as "easy" as it may seem. Regards, Terje

"Terje Slettebø" <tslettebo@broadpark.no> wrote #include <boost/operator_traits/has_plus_op.hpp> #include <boost/operator_traits/has_minus_op.hpp> #include <boost/concept_traits/std/is_assignable.hpp> template<class T1,class T2> struct is_my_concept : mpl::and_< is_assignable<T1>, is_assignable<T2>, has_plus_op<T1, T2>, has_minus_op<T1, T2>
{};
Why not : has_op< T1,plus, T2> ? hence: #include <boost/operator_traits/has_ops.hpp> template<class T1, class Opa, class Opb,class T2> struct is_my_concept : mpl::and_< is_assignable<T1>, is_assignable<T2>, has_op<T1, Opa,T2>, has_op<T1, Opb, T2>
{};
eg + - * / believe me this saves a lot of coding. regards Andy Little

"Andy Little" <andy@servocomm.freeserve.co.uk> wrote in message news:ci7vcj$4db$1@sea.gmane.org... Hmm Apologies. Scratch the above. I guess its not that relevent here. Anyways, after taking a look at the library(s)... it looks extremely impressive. Does it duplicate type_traits... maybe.. but as well as extending it, it also provides encapsulation of commonly used concepts. As a newbie to enable_if, I find this extremely interesting. For those not up to speed on enable_if. It provides effectively a way to switch on or switch off functions and classes based on their parameters. This saves an awful lot of work over specialising things for every case. In effect enable_if enables you to specialise a family of types all at one go. Once you have enable_if then you certainly need tools to work with it and from an initial view of the docs, many common but often hard to figure concepts are available here in encapsulated form. For myself I dont want to know the gory details... so long as it works. Like It.. Like it a Lot. :-) Perhaps this is even another major milestone in the development of C++ ! regards Andy Little

From: "Andy Little" <andy@servocomm.freeserve.co.uk>
"Andy Little" <andy@servocomm.freeserve.co.uk> wrote in message news:ci7vcj$4db$1@sea.gmane.org...
Hmm Apologies. Scratch the above. I guess its not that relevent here.
Anyways, after taking a look at the library(s)... it looks extremely impressive.
Thanks. :) All the work I've put into it, is worth it, when one gets feedback like this.
Does it duplicate type_traits... maybe..
I'm not sure if I understand. None of the traits in the library(s) does the same as the Boost type traits (that wouldn't be any point). It _uses_ those traits (as well as other components) to build new traits, yes, so it extends it, as you say. In fact, that was one thing I found afterwards that didn't give proper credit to: The Boost type traits are indispensable to the library. Without them, there would be no library.
but as well as extending it, it also provides encapsulation of commonly used concepts.
That, it does, as well as having facilities to create traits for your own concepts. (Those same facilities that are used to create the C++ standard and MPL traits), analoguous to the Boost Concept Check Library, as mentioned in another posting. I see, however, that I need to improve the docs (no surprise, there, it was preliminary, after all), to focus more on how one might make one's own traits. As it is, it's mostly just introduction, and a reference section. I agree with what has come from these threads that the ability to provide automation, or elementary building blocks, as well as making that easy to use, is crucial for such a library.
Once you have enable_if then you certainly need tools to work with it and from an initial view of the docs, many common but often hard to figure concepts are available here in encapsulated form. For myself I dont want to know the gory details... so long as it works.
That's much of the point:
Like It.. Like it a Lot. :-)
Thanks. :) Now, if I can find some time to play with it, myself (eating your own dog food, as they say). I certainly intend to do that. It was not at least about "scratching an itch", and I ironically got lots of long, rambling error messages from time to time when working on it, so that certainly helped to motivate me to keep going. I pondered whether I should "bootstrap" it - that is, use the traits in the library for things like the unit tests. However, the unit tests take long enough to compile as they are, and I didn't want to possibly substantially prolong it, by adding tests to the templates there. So I just took the "hits" that came, when something went wrong, and I got an instantiation stack dump pages and pages long, and just prayed that it would be reasonably easy to fix the problem. When your software development methodology is based on prayer, and hanging good-luck charms on the monitor, something is wrong, isn't it? ;) Yet, I think I'm not alone in this... Unfortunately, my system clock was wrong in my first few postings, and that also included the files in the library, making it rather impossible to compile (the generated files would always be older than the source files...). I've uploaded a new version where all the time stamps have been reset to normal values. Sorry about that. Regards, Terje

From: "Andy Little" <andy@servocomm.freeserve.co.uk>
"Terje Slettebø" <tslettebo@broadpark.no> wrote
#include <boost/operator_traits/has_plus_op.hpp> #include <boost/operator_traits/has_minus_op.hpp> #include <boost/concept_traits/std/is_assignable.hpp>
template<class T1,class T2> struct is_my_concept : mpl::and_< is_assignable<T1>, is_assignable<T2>, has_plus_op<T1, T2>, has_minus_op<T1, T2>
{};
Why not :
has_op< T1,plus, T2> ?
hence:
#include <boost/operator_traits/has_ops.hpp>
template<class T1, class Opa, class Opb,class T2> struct is_my_concept : mpl::and_< is_assignable<T1>, is_assignable<T2>, has_op<T1, Opa,T2>, has_op<T1, Opb, T2>
{};
eg + - * /
believe me this saves a lot of coding.
Thanks for your thoughts. The problem is that, despite what one might think, the implementation of the traits to test for +, - * and / are different, sometimes subtly, sometimes more. Have a look, this is has_plus_op: BOOST_OT_DETAIL_DEFINE_BINOP_TRAIT(has_plus_op_impl, +) 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>
{};
and this is has_multiply_op: BOOST_OT_DETAIL_DEFINE_BINOP_TRAIT(has_multiply_op_impl, *) template<class T,class U = T> struct has_multiply_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> >, detail::either_is_class_or_union_and_not_void<T,U> >, detail::has_multiply_op_impl<T,U>
{};
Can you see the difference? ;) Even for + and -, there are differences, such as that you can subtract two pointers, but not add them. For this reason, I felt it was best that each trait has a separate file, and that avoids including more than necessary. For the cases where there's reasonable commonality between the traits (such as the comparision operators), the commonality is factored out into a header in the detail-directory (and namespace), which is included in the relevant trait headers. In any case, one can always do: #include <boost/operator_traits/operator_traits.hpp> to get all the operator traits (same way to get the std concept traits, and the MPL concept traits), just like Boost type traits. However, that drags in everything, so unless you're doing a unit test on it all, it could seriously degrade compilation time doing that. I'm not sure if I answered your concerns, and if I didn't, please let me know. Regards, Terje

At Saturday 2004-09-18 03:44, Terje Slettebø wrote: [deleted]
Even for + and -, there are differences, such as that you can subtract two pointers, but not add them.
The erroneous assumption that this is true is something that has bothered me for some time (and indeed I have worked on systems where it simply isn't true, that the result of a+b would be useful even if both were pointers). In an abstract sense it's not true on any system. Consider: T* a; T* b; .... get them assigned meaningfully to something in a sequence "container" The midpoint between them is (this is legal using your constraints): a + (b-a)/2 1st year algebra teaches us that this is equivalent to: (a+b)/2 While on most systems the intermediate value (a+b) would be useless, as soon as you divide by 2 it again becomes useful. with a third pointer (T* c;): (a+b+c)/3 becomes calculable and useful though clearly (a+b+c)/2 is NOT useful. Due to the architecture of the machine we were building, we were forced to examine this rather carefully and, of course, our assembler handled expressions of this nature. We used a system somewhat like the one described by Scott Meyers in his forward to "Modern C++ Design" where he references a 1995 C++ Report article by John Barton and Lee Nackman which posits a template solution to perform typesafe dimensional analysis with zero runtime cost. We had only two dimensions to worry about (memoryness and relocatableness), but it was up and running in the mid '70s. I don't mean to pick on you in particular. I just got tired of hearing people repeat the "mantra" of "you can subtract two pointers, but not add them". It's simply NOT true. It has also been somewhat of a disappointment that the HLL folks (and C in particular (you can program right down to the sand) have managed to ignore this) I'll also note, in passing, that for integers the two are equivalent also, but very few compilers will generate code to guarantee the correct result if you attempt to calculate (a+b)/2 which _does_ exist on every computer out there. [deleted] Victor A. Wagner Jr. http://rudbek.com The five most dangerous words in the English language: "There oughta be a law"

"Victor A. Wagner Jr." <vawjr@rudbek.com> writes:
I don't mean to pick on you in particular. I just got tired of hearing people repeat the "mantra" of "you can subtract two pointers, but not add them". It's simply NOT true.
Well, the nitpicking seems a bit unwarranted since it certainly is true in C++. It all depends on context, doesn't it? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

At Saturday 2004-09-25 05:47, you wrote:
"Victor A. Wagner Jr." <vawjr@rudbek.com> writes:
I don't mean to pick on you in particular. I just got tired of hearing people repeat the "mantra" of "you can subtract two pointers, but not add them". It's simply NOT true.
Well, the nitpicking seems a bit unwarranted since it certainly is true in C++. It all depends on context, doesn't it?
what's true, you can't add pointers? that's a rule written by some folks that has NO bearing on reality as my message showed. A rather pointless rule at that. did you actually understand the argument?
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Victor A. Wagner Jr. http://rudbek.com The five most dangerous words in the English language: "There oughta be a law"

"Victor A. Wagner Jr." <vawjr@rudbek.com> writes:
At Saturday 2004-09-25 05:47, you wrote:
"Victor A. Wagner Jr." <vawjr@rudbek.com> writes:
I don't mean to pick on you in particular. I just got tired of hearing people repeat the "mantra" of "you can subtract two pointers, but not add them". It's simply NOT true.
Well, the nitpicking seems a bit unwarranted since it certainly is true in C++. It all depends on context, doesn't it?
what's true, you can't add pointers?
In C++, you can't add pointers.
that's a rule written by some folks that has NO bearing on reality as my message showed. A rather pointless rule at that.
Maybe. It doesn't make much sense until you divide by 2 ;-).
did you actually understand the argument?
Of course. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

From: "Victor A. Wagner Jr."
At Saturday 2004-09-18 03:44, Terje Slettebø wrote:
Even for + and -, there are differences, such as that you can subtract two pointers, but not add them.
The erroneous assumption that this is true is something that has bothered me for some time (and indeed I have worked on systems where it simply isn't true, that the result of a+b would be useful even if both were pointers). In an abstract sense it's not true on any system.
Consider:
T* a; T* b; .... get them assigned meaningfully to something in a sequence "container"
The midpoint between them is (this is legal using your constraints):
a + (b-a)/2
1st year algebra teaches us that this is equivalent to:
(a+b)/2
While this is interesting (and I understand it was not meant to pick on me in particular, as you said, and it wouldn't really make any sense, as Dave Abrahams pointed out), my point was strictly about what is legal _C++_, and the above is not legal C++, which I think you will agree with: "5.7 Additive operators [...] For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type." The context was detecting addability, and if the trait had reported "yes" for pointers, it would have caused errors elsewhere. You might also argue that the language should allow the expression above, but that's a different discussion.
I'll also note, in passing, that for integers the two are equivalent also, but very few compilers will generate code to guarantee the correct result if you attempt to calculate (a+b)/2 which _does_ exist on every computer out there.
With "correct result", I guess you mean the possibility of overflow in this expression, compared to the other form. Regards, Terje

At Sunday 2004-09-26 02:19, you wrote:
From: "Victor A. Wagner Jr."
At Saturday 2004-09-18 03:44, Terje Slettebø wrote:
Even for + and -, there are differences, such as that you can subtract two pointers, but not add them.
The erroneous assumption that this is true is something that has bothered me for some time (and indeed I have worked on systems where it simply isn't true, that the result of a+b would be useful even if both were pointers). In an abstract sense it's not true on any system.
Consider:
T* a; T* b; .... get them assigned meaningfully to something in a sequence "container"
The midpoint between them is (this is legal using your constraints):
a + (b-a)/2
1st year algebra teaches us that this is equivalent to:
(a+b)/2
While this is interesting (and I understand it was not meant to pick on me in particular, as you said, and it wouldn't really make any sense, as Dave Abrahams pointed out), my point was strictly about what is legal _C++_, and the above is not legal C++, which I think you will agree with:
"5.7 Additive operators [...] For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type."
The context was detecting addability, and if the trait had reported "yes" for pointers, it would have caused errors elsewhere.
You might also argue that the language should allow the expression above, but that's a different discussion.
yes... I was trying to catch up on EMail while waiting for the regression test to fail for mingw and just responded. It's a different discussion, and yes, I believe the language is 'in error'.
I'll also note, in passing, that for integers the two are equivalent also, but very few compilers will generate code to guarantee the correct result if you attempt to calculate (a+b)/2 which _does_ exist on every computer out there.
With "correct result", I guess you mean the possibility of overflow in this expression, compared to the other form.
either form can overflow, (different circumstances, of course) therefore the simple "average two things" just isn't all that easy in C++ (or C...or any other HLL I'm aware of)
Regards,
Terje
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Victor A. Wagner Jr. http://rudbek.com The five most dangerous words in the English language: "There oughta be a law"

Victor A. Wagner Jr. <vawjr@rudbek.com> wrote: The midpoint between them is (this is legal using your constraints):
a + (b-a)/2
1st year algebra teaches us that this is equivalent to:
(a+b)/2
While on most systems the intermediate value (a+b) would be useless, as soon as you divide by 2 it again becomes useful.
with a third pointer (T* c;):
(a+b+c)/3
becomes calculable and useful though clearly
Just a small $0.02... (a + b) is a dangerous thing in a 32 bit world full of arithmetic overflow opportunities. You'd have to have a very good reason for doing it this way I'd think. a + (a-b) / 2 is a bit safer. matt matthurd@acm.org

At Sunday 2004-09-26 17:35, you wrote:
Victor A. Wagner Jr. <vawjr@rudbek.com> wrote: The midpoint between them is (this is legal using your constraints):
a + (b-a)/2
1st year algebra teaches us that this is equivalent to:
(a+b)/2
While on most systems the intermediate value (a+b) would be useless, as soon as you divide by 2 it again becomes useful.
with a third pointer (T* c;):
(a+b+c)/3
becomes calculable and useful though clearly
Just a small $0.02...
(a + b) is a dangerous thing in a 32 bit world full of arithmetic overflow opportunities. You'd have to have a very good reason for doing it this way I'd think.
a + (a-b) / 2 is a bit safer
I don't see the safety issue and what you showed is incorrect; a + (b-a) / 2 is at least correct, but b-a overflows just as readily as a+b
matt matthurd@acm.org _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Victor A. Wagner Jr. http://rudbek.com The five most dangerous words in the English language: "There oughta be a law"

Victor A. Wagner Jr. <vawjr@rudbek.com> wrote:
I don't see the safety issue and what you showed is incorrect; a + (b-a) / 2 is at least correct, but b-a overflows just as readily as a+b
oops, for sure, b-a rather. I'm missing some understanding, given that b >= a, why is b-a just as ready to overflow as a+b? I guess you are saying b - a > 2GB (typical ptrdiff_t) is just as likely as an overflow from a + b, if it was legal, on a common 32 bit platform for example, or have I misunderstood? matt. matthurd@acm.org

OOps At Monday 2004-09-27 01:02, you wrote:
At Sunday 2004-09-26 17:35, you wrote:
Victor A. Wagner Jr. <vawjr@rudbek.com> wrote: The midpoint between them is (this is legal using your constraints):
a + (b-a)/2
1st year algebra teaches us that this is equivalent to:
(a+b)/2
While on most systems the intermediate value (a+b) would be useless, as soon as you divide by 2 it again becomes useful.
with a third pointer (T* c;):
(a+b+c)/3
becomes calculable and useful though clearly
Just a small $0.02...
(a + b) is a dangerous thing in a 32 bit world full of arithmetic overflow opportunities. You'd have to have a very good reason for doing it this way I'd think.
a + (a-b) / 2 is a bit safer
I don't see the safety issue and what you showed is incorrect; a + (b-a) / 2 is at least correct, but b-a overflows just as readily as a+b
I should know better than to try this stuff at 0100 local. Of course (b-a) is less likely to overflow than (a+b). It's certainly fair to presume that the _size_ of any such container will be small in comparison to the range addresses
matt matthurd@acm.org _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Victor A. Wagner Jr. http://rudbek.com The five most dangerous words in the English language: "There oughta be a law" _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Victor A. Wagner Jr. http://rudbek.com The five most dangerous words in the English language: "There oughta be a law"

On 9/25/04 6:16 AM, "Victor A. Wagner Jr." <vawjr@rudbek.com> wrote: [SNIP]
I don't mean to pick on you in particular. I just got tired of hearing people repeat the "mantra" of "you can subtract two pointers, but not add them". It's simply NOT true.
Actually, it IS true... From a certain point of view. That's because pointers are NOT (conceptually) numbers, they're LOCATIONS! Addition among them makes no sense. Technically, subtraction between them would be equally senseless. But memory locations are treated as being linearly arranged in an order. Therefore we can define subtraction as the OFFSET one location is linearly from another. So when you take the midpoint of some pointers... T *a, *b, *c; ..you're taking the average of their offsets (from a defined origin). T * const base = a; T * const average = base + ((a - base) + (b - base) + (c - base)) / 3;
It has also been somewhat of a disappointment that the HLL folks (and C in particular (you can program right down to the sand) have managed to ignore this) [TRUNCATE]
Or maybe they didn't ignore it to the point of an arbitrary mantra, but had a rationale for not treating pointers as simple numbers. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

Terje Slettebø <tslettebo@broadpark.no> writes:
enable_if doesn't always improve error messages. Sometimes you get a long list of candidate functions when what you really wanted was a message from inside just one of the overloads that indicates how concept conformance was violated.
How can you get a list of candidate functions, if enable_if excludes them from the overload set? Could you give a code example?
#include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_same.hpp> template <class T> typename boost::enable_if<boost::is_same<T,int>,int>::type f(T); template <class T> typename boost::enable_if<boost::is_same<T,long*>,int>::type f(T); int main() { f("foo"); } CWPro9: ### mwcc Compiler: # File: foo.cpp # ---------------- # 14: f("foo"); # Error: ^ # function call 'f({lval} const char[4])' does not match # 'f<...>(__T0)' # 'f<...>(__T0)' VC7.1: foo.cpp foo.cpp(14) : error C2893: Failed to specialize function template 'boost::enable_if< boost::is_same<T, long *>, int >::type f(T)' With the following template arguments: 'const char *' foo.cpp(14) : error C2893: Failed to specialize function template 'boost::enable_if< boost::is_same<T, int>, int >::type f(T)' With the following template arguments: 'const char *' And even when there's no list as with GCC, "no match" is usually less-useful than "here's what's wrong with the argument." -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Terje Slettebø <tslettebo@broadpark.no> writes:
However, the invention of enable_if [HJW03] [JWL03] (now part of Boost) opened the door to overloading based on arbitrary properties of types. Now, what we needed was techniques for detecting type properties, which could be used to create "concept type traits". Using this, we could have done, e.g. for std::sort:
template<class Iterator> typename enable_if<is_random_access_iterator<Iterator> >::type sort(Iterator begin, Iterator end) { // ... }
I should have pointed to this example more specifically in my previous message. If you had a sort overload for bidirectional iterators using this technique, you'd have an ambiguity. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

From: "David Abrahams" <dave@boost-consulting.com>
Terje Slettebø <tslettebo@broadpark.no> writes:
However, the invention of enable_if [HJW03] [JWL03] (now part of Boost) opened the door to overloading based on arbitrary properties of types. Now, what we needed was techniques for detecting type properties, which could be used to create "concept type traits". Using this, we could have done, e.g. for std::sort:
template<class Iterator> typename enable_if<is_random_access_iterator<Iterator> >::type sort(Iterator begin, Iterator end) { // ... }
I should have pointed to this example more specifically in my previous message. If you had a sort overload for bidirectional iterators using this technique, you'd have an ambiguity.
Indeed. That was also noted in the OP (about "best match", and my std::advance example showed the manual disambiguation needed in the presence of concept hierarchies). To get overloading on concept with "best match" appears to need a language change (such as suggested in the papers by Bjarne Stroustrup and Gabriel Dos Reis), or at least be very intrusive on the functions (such as taking an extra parameter), for example: #include <iostream> struct bidirectional_iterator {}; struct random_access_iterator : bidirectional_iterator {}; void sort(bidirectional_iterator) { std::cout << "sort(bidirectional_iterator)\n"; } //void sort(random_access_iterator) { std::cout << "sort(random_access_iterator)\n"; } int main() { sort(random_access_iterator()); } As it stands, it prints "sort(bidirectional_iterator)". However, if we uncomment the above line, it prints "sort(random_access_iterator)", showing that it's a better match. This avoids changing existing overloads, to add better/worse match functions, but it requires an extra parameter. Regards, Terje

Terje Slettebø wrote:
Colleagues:
Are you tired of long and incomprehensible error messages when using STL, MPL, or some other modern template library? So am I!
So are we! Keep em coming Terje! I'm keeping a watchful eye on this. Spirit-2 is already in the drawing board and such a facility will be an indespensible partner. I'll be lurking :-) Cheers, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

From: "Joel" <joel@boost-consulting.com>
Terje Slettebø wrote:
Colleagues:
Are you tired of long and incomprehensible error messages when using STL, MPL, or some other modern template library? So am I!
So are we! Keep em coming Terje! I'm keeping a watchful eye on this. Spirit-2 is already in the drawing board and such a facility will be an indespensible partner. I'll be lurking :-)
Thanks for the encouragement, it means a lot to me, especially coming from someone like you. Several interesting ideas have come up in this (and the related "is_dereferenceable" thread), and I intend to look at it all, and see what we can learn from it, from each other. The current state with regards to such facilities is mostly one of experimentation, and it was not at least to get feedback, and seeing other approaches, that I brought it up (or joined the discussion, as it were). Regards, Terje

Hi Terje, I had a look at your library proposal today. Looks like a very valuable tool for everyone into generic programming. It seems quite easy to add new concepts, which I think is one of the most important things (the has_member family looks very promising). I was able to build the operator_traits test with GCC 3.2.3 mingw (about 20 minutes PIII,600MHz,512MB RAM - 100% passed - guess I just had to see it with my own eyes ;+). It failed to build on MSVC 13.10.3077 and Borland 5.4.6 (I kept the output, in case you are interested). It might be possible to put the prefiltering stage of the operator traits into sort of table based form, making it easier to maintain, eliminating a lot of inclusion dependencies and template instantiations and covering tests for builtin operators (if they should be detected, that is) as well. This can probably be done with a technique in some way similar to the one applied to the operator checking itself - defining a rule set of overloaded functions to match the exclusions. Classes with a converting copy constructor can be used to categorize the types matched by the rules. The rule sets for different operator categories could be put into nested namespaces so inner categories could 'inherit' the rules of the outter ones. I attached some experimental source code for the prefiltering of an operator+ check to clearify the basic idea (compiles with GCC and MSVC and CVS version of boost). However, I am looking forward to using your library. It's late here now - so that's it for today. Best regards, Tobias

Tobias Schwinger wrote:
I attached some experimental source code
Hrm... Did I ? ;+) Here's the missing attachment #include <boost/config.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/eval_if.hpp> #include <boost/static_assert.hpp> #include <boost/type_traits/config.hpp> #include <boost/type_traits/add_reference.hpp> using namespace boost; ///////////////////////////////////////////////////////////////////////////// // Type categories struct integral { integral(char); integral(short); integral(int); integral(long); }; struct real { real(float); real(double); }; struct pointer { template <typename T> pointer(T*); }; ///////////////////////////////////////////////////////////////////////////// // States of the filter enum { pass = 1, filter_true = 2, filter_false = 3 }; typedef char (& pass_type ) [pass]; typedef char (& filter_true_type ) [filter_true]; typedef char (& filter_false_type ) [filter_false]; ///////////////////////////////////////////////////////////////////////////// // Filtering rule set pass_type BOOST_TT_DECL filter(...); // type combinations matching builtin types filter_true_type filter(integral,integral); filter_true_type filter(integral,pointer); filter_true_type filter(integral,real); filter_true_type filter(real,integral); filter_true_type filter(real,real); filter_true_type filter(pointer,integral); // impossible type combinations filter_false_type filter(real,pointer); filter_false_type filter(pointer,real); filter_false_type filter(pointer,pointer); ///////////////////////////////////////////////////////////////////////////// // The filter implementation template <typename T1, typename T2, typename Operation> struct op_test_input_filter { private: static typename add_reference<T1>::type arg1; static typename add_reference<T2>::type arg2; BOOST_STATIC_CONSTANT(int, state = ( sizeof(filter(arg1, arg2)) )); public: typedef typename mpl::eval_if_c< (state == pass) , typename Operation::apply<T1,T2> , mpl::bool_<state == filter_true> >::type type; BOOST_STATIC_CONSTANT(bool, value = ( type::value )); }; // Specializations for void arguments could come here. ///////////////////////////////////////////////////////////////////////////// // Test for the filter implementation above // These are used in place of a "real" operator checker to test the filtering // behaviour. struct always // ::type is always mpl::bool_<true> regardless of the argumetns { template <typename T1, typename T2> struct apply { typedef mpl::bool_<true> type; }; }; struct never // ::type is always mpl::bool_<false> regardless of the arguments { template <typename T1, typename T2> struct apply { typedef mpl::bool_<false> type; }; }; struct illegal // is not instantiatable { template <typename T1, typename T2> struct apply { BOOST_STATIC_ASSERT(false); }; }; // If the combination of type arguments is matched by the filter, the result // must not depend on the specified operation. #define ASSERT_FILTER(type1,type2) \ BOOST_STATIC_ASSERT(( op_test_input_filter<type1,type2,always>::value \ == op_test_input_filter<type1,type2,never>::value )) // The combination of type arguments is matched by the filter and the result // is "true". The operation passed results in an error when instantiated. #define ASSERT_FILTER_TRUE_RESULT(type1,type2) \ ASSERT_FILTER(type1,type2); \ BOOST_STATIC_ASSERT(( op_test_input_filter<type1,type2,illegal>::value )) // The combination of type arguments is matched by the filter and the result // is "false". The operation passed results in an error when instantiated. #define ASSERT_FILTER_FALSE_RESULT(type1,type2) \ ASSERT_FILTER(type1,type2); \ BOOST_STATIC_ASSERT(!( op_test_input_filter<type1,type2,illegal>::value )) // If the combination of type arguments is unmatched by the filter, the // result must only depend on the specified operation. #define ASSERT_PASS(type1,type2) \ BOOST_STATIC_ASSERT(( op_test_input_filter<type1,type2,always>::value \ != op_test_input_filter<type1,type2,never>::value )) struct foo { }; struct bar { }; int main() { ASSERT_PASS(foo,bar); ASSERT_PASS(foo,foo); ASSERT_FILTER_TRUE_RESULT(int,int); ASSERT_FILTER_TRUE_RESULT(char,int); ASSERT_FILTER_TRUE_RESULT(char,char); ASSERT_FILTER_TRUE_RESULT(int,int*); ASSERT_FILTER_FALSE_RESULT(int*,float); ASSERT_FILTER_FALSE_RESULT(int*,int*); }

From: "Terje Slettebø"
From: "Tobias Schwinger"
I attached some experimental source code for the prefiltering of an operator+ check to clearify the basic idea (compiles with GCC and MSVC and CVS version of boost).
It works indeed with GCC and MSVC, but Intel C++ 7.1 had some serious problems with it. The following is one of several of the same kind of error messages I got:
I've now looked at the implementation, and I'm afraid Intel C++ (EDG) might be right. :/ Here's a cut-down version showing the problem: struct integral { integral(int); }; struct real { real(double); }; int filter(integral); int filter(real); int main() { int state = sizeof(filter(1)); // filter(int) } On Comeau C++ online it gives the same error, not surprisingly: "ComeauTest.c", line 21: error: more than one instance of overloaded function "filter" matches the argument list, the choices that match are: function "filter(integral)" function "filter(real)" The argument types that you used are: (int) int state = sizeof(filter(1)); // filter(int) ^ We have two viable conversion sequences: int -> integral int -> double -> real They both have the same form - user-defined conversion sequence (13.3.3.1/3). However, I can't find any case in 13.3.3.2/3 that says that one is better than the other of the above. If anyone finds otherwise, please post chapter and verse. Another problem with this approach can be demonstrated by listing the cases that are checked by the operator+ detection: - Both types being arithmetic or enum, or - One is array and the other is integral or enum, or - Either is class, and the other is anything but void Even if we found a way to give a numerical code or something to each fundamental type (typical typeof-implementation), there's still enum, so we need some kind of is_enum-detection (which would likely require the Boost one). The same goes for detecting class or union. We also need a way to express the above conditions. I haven't found a shorter way of specifying this, than the current has_plus_op<> definition (operator_traits/has_plus_op.hpp). Regards, Terje

Terje Slettebø wrote:
From: "Terje Slettebø"
From: "Tobias Schwinger"
I attached some experimental source code for the prefiltering of an operator+ check to clearify the basic idea (compiles with GCC and MSVC and CVS version of boost).
It works indeed with GCC and MSVC, but Intel C++ 7.1 had some serious problems with it. The following is one of several of the same kind of
Terje, first of all thanks for your time and for your corrections. However, I still think there is nothing wrong with the basic idea, just with my sloppy example implementation of it. I should definitely buy a more conformant compiler to recognize such things before... With some refinement (see below) this could be a very attractive (also see below) alternative to the "metaprogramming boilerplate" used to force things into shape. Refinement of the example code: ------------------------------- The type categorization could be done like this: struct integral_or_enum { }; struct real_number { }; struct pointer_or_array { }; struct class_or_union { }; class_or_union type_category(...); integral_or_enum type_category(char); integral_or_enum type_category(short); integral_or_enum type_category(int); integral_or_enum type_category(long); real_number type_category(float); real_number type_category(double); template <typename T> pointer_or_array type_category(T const *); Used like this: BOOST_STATIC_CONSTANT(int, state = ( sizeof(filter(type_category(arg1) ,type_category(arg2) )) )); This way the ambiguity problems vanish (tested the overloaded cascade with Comeau-online, just in case ;+). Problem discussion: -------------------
Another problem with this approach can be demonstrated by listing the cases that are checked by the operator+ detection:
- Both types being arithmetic or enum, or
I renamed the type category 'integral' to 'integral_or_enum' because of 7.2-8 (enum), 4.5-2 (integral promotion).
- One is array and the other is integral or enum, or
I renamed the type category 'pointer' to 'integral_or_array' because of 13.3.3.1.1 (overloading - standard conversion sequences) Tab. 9. If there is a need to distiguish between (object) pointer and array this needs extra care (see below), sure. However, has_plus_op applies these tests disjunctively.
- Either is class, and the other is anything but void
A match defines an exclusion - no match means "pass the filter" - and this would correctly happen in this case.
Even if we found a way to give a numerical code or something to each fundamental type (typical typeof-implementation), there's still enum, so we need some kind of is_enum-detection (which would likely require the Boost one). The same goes for detecting class or union. We also need a way to express the above conditions. I haven't found a shorter way of specifying this, than the current has_plus_op<> definition (operator_traits/has_plus_op.hpp).
Can't see an "is_enum" - only "is_enum_or_integral" and this is much easier. Can't see any distinction between class and union in has_plus_op.hpp, either. The strenght of the overload approach lies in the natural way of grouping types - can't see what separation of integral types should help. I'm not sure I get your point. Am I making any naive assumptions here ? Am I misreading anything ? Please, correct me if I'm wrong ! Real world scenario / further suggestions: ------------------------------------------ 1 A "real world implementation" will need yet another filtering stage: The argument creation (currently only add_reference is applied) can be changed to achieve this (so in place of just add_reference *any* metafunction could be applied in its place). A tagging type can be created for exceptional cases which can then be caught with a type_category overload. In fact, I believe there is no way around it, if the behaviour of these traits should be defined for function and member pointers. With this approach it is checked only _once_, however. 2 It is possible to group the category tagging types by inheritance in addition to using namespaces to elegantly arrange the rule set (as mentioned in the previous post): // Note: this is just an example to clearify the text // it's *not* a very good grouping here struct anything { }; struct class_or_union : anything { }; struct numeric_or_pointer : anything { }; struct numeric : numeric_or_pointer { }; struct pointer_or_array : numeric_or_pointer { }; struct integral_or_enum : numeric { }; struct real_number : numeric { }; 3 All operators of one group having the same prefiltering rules can share one filter. The filter implementation can be shared, too - _excpet_ the sizeof expression and a using statement to select the rule set. Why is this approach more attractive than the current implementation ? ---------------------------------------------------------------------- 1. It exploits the very nature of the type system instead of brute forcing around it in order to emulate it afterwards These metafunctions exclude cases that are naturally easier to detect together (like class/union, enum/integral). Afterwards they are or-ed so the exclusion (which did cost us probably more metafunction invocations than the actual detection) has been done for nothing. Nothing wrong with this - but not very efficient if all we want from them is a model of categories natural to the C++ type system. Overthis, some metafunction might contain lengthy workarounds for specific compiler and every one of them tests for cv qualified void. 2. It depends on just a few headers ("driver code" and rule set) Specification of portability becomes much easier. The circumstances under which a specific metafunction works become harder to track the more transitive dependencies arise. 3. It is resource friendly It takes up less compiler memory, needs less filesystem access and is likely to be less complex. These aspect will especially carry weight for the compilation time of projects split into a lot of small compilation units. 4. It is easier portable Once the "driver code" runs correctly on a platform, _all_ filtering runs. Because it does depend on just a few metafunctions it's less tedious to check their documentation/source if they exist/work/return the right result on the target compiler. Best regards, Tobias

Tobias Schwinger wrote:
class_or_union type_category(...);
integral_or_enum type_category(char); integral_or_enum type_category(short); integral_or_enum type_category(int); integral_or_enum type_category(long);
real_number type_category(float); real_number type_category(double);
This is still confusable by a class with implicit conversion operator. Using a pointer to these types instead, however, solves this problem.

Tobias Schwinger wrote:
Tobias Schwinger wrote:
class_or_union type_category(...);
integral_or_enum type_category(char); integral_or_enum type_category(short); integral_or_enum type_category(int); integral_or_enum type_category(long);
real_number type_category(float); real_number type_category(double);
This is still confusable by a class with implicit conversion operator. Using a pointer to these types instead, however, solves this problem.
... and breaks coverage for enum types. There are ways to make it work again (see previous message at another position of this thread). I need some time to think about how to merge it in elegantly, though.

From: "Tobias Schwinger"
Terje Slettebø wrote:
From: "Terje Slettebø"
From: "Tobias Schwinger"
I attached some experimental source code for the prefiltering of an operator+ check to clearify the basic idea (compiles with GCC and MSVC and CVS version of boost).
It works indeed with GCC and MSVC, but Intel C++ 7.1 had some serious problems with it. The following is one of several of the same kind of
first of all thanks for your time and for your corrections.
However, I still think there is nothing wrong with the basic idea, just with my sloppy example implementation of it.
By all means, like I said in the posting before it, I find it an interesting idea, and was just as "disappointed" as you to find out that it might not be possible to do it that way, and if there's better way to do the pre-filtering than the "metaprogramming boilerplate", then I'm all for it.
Refinement of the example code: -------------------------------
<snip> Great work! :) This actually works, also on Intel C++/Comeau C++. It's good that you didn't give up on the first try (not that I expected you to. :) ).
Problem discussion: -------------------
Another problem with this approach can be demonstrated by listing the cases that are checked by the operator+ detection:
- Both types being arithmetic or enum, or
I renamed the type category 'integral' to 'integral_or_enum' because of 7.2-8 (enum), 4.5-2 (integral promotion).
Good point. What we are looking for is things that could be used in a certain context, e.g. "a + b". This means that not only e.g. arithmetic types match, but also something convertible to them, as well as reference to them.
- One is array and the other is integral or enum, or
I renamed the type category 'pointer' to 'integral_or_array' because of 13.3.3.1.1 (overloading - standard conversion sequences) Tab. 9.
As long as it passes the tests... :) I'll study this more carefully. :) I look (hopefully) forward to a complete code example for has_plus_op<> using this technique, if you could have made something like that.
If there is a need to distiguish between (object) pointer and array this needs extra care (see below), sure. However, has_plus_op applies these tests disjunctively.
- Either is class, and the other is anything but void
A match defines an exclusion - no match means "pass the filter" - and this would correctly happen in this case.
Even if we found a way to give a numerical code or something to each fundamental type (typical typeof-implementation), there's still enum, so we need some kind of is_enum-detection (which would likely require the Boost one). The same goes for detecting class or union. We also need a way to express the above conditions. I haven't found a shorter way of specifying this, than the current has_plus_op<> definition (operator_traits/has_plus_op.hpp).
Can't see an "is_enum" - only "is_enum_or_integral" and this is much easier.
True... Good thinking. :) (So it was useful to refactor those tests (like enum-or-integral) into sub-components, after all - it made what being tested for easier to see. :) It might otherwise have got lost in a sea of syntactic noise).
Can't see any distinction between class and union in has_plus_op.hpp, either.
True, again...
The strenght of the overload approach lies in the natural way of grouping types - can't see what separation of integral types should help.
I'm not sure I get your point.
I think you got it - that the previous example didn't compile on some compilers, and that I wondered if it was possible to cover the same cases better - and I think you have convincingly addressed it. :)
Am I making any naive assumptions here ? Am I misreading anything ?
Please, correct me if I'm wrong !
No, I think you're right on. :) Thanks for your very thorough investigation into this. I'll consider your following points carefully. In the mean time, as you suggested in an email, if you could have made this into a polished boostified version, that would have been great. As a proof-of-concept (no pun intended. :) ), as mentioned, maybe you could have written a has_plus_op<> replacement using this technique, which passes the tests, since has_plus_op<> contains a reasonable amount of "boilerplate", so it could be useful to test with. This is remniscent of when I worked on an improved version of lexical_cast a while ago (to support different character types, etc.), and had long if-else switch-on-type chains, and Kevlin Henney instead found a way to do it with much simpler code (which is the current version of lexical_cast). Regards, Terje

From: "Terje Slettebø"
From: "Tobias Schwinger"
Thanks for your very thorough investigation into this. I'll consider your following points carefully. In the mean time, as you suggested in an email, if you could have made this into a polished boostified version, that would have been great.
To just make an adjustment to this: I used the term "polished boostified" version, as you mentioned that in the mail, but it's actually not necessary at this point (although it would be just fine), since we're exploring. 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. Regards, Terje

Terje Slettebø wrote:
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 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"). 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 (*) 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. If I did not forget anything, it should be sufficient to implement builtin operator detection on top of it, too. Here's a list of the leaves in the categorization inheritance graph (i.e. the finest level of separation): Non object types: function (function - not function pointer) void_type (represents void) Each LValue and RValue of these object types: aggregate (class or union) enumeration floating integral object_ptr (pointer to object type) special_ptr (pointer to member, function or member function) On MSVC (in ANSI-mode, that is) for example, it does not need a single Type Traits class to accomplish this. Note that the 'is_plus_op' test uses the full categorization - so there is no extra cost in performance. Regards, Tobias P.S.: There is a note within the Type Traits ICE workaround documentation about Metrowerks having problems using sizeof on the result type of an overloaded function (dated 2001) - is this still true for later / the current version ?

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

Terje Slettebø wrote:
[...]
I'm glad you like it ! Thanks for all the positive feedback ;+).
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. ;)
Beware! It might be Intel (or EDG) is not 100% supported out of the box. However it's a matter of seconds to find out and likely to be a matter of minutes to adjust it, if necessary. See the "how to port" part in the "readme.txt".
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.
It's really astonishing - I am suprised, too. I refined the 'is_enum' hack I posted (somewhere in this thread) until its behaviour was aequivalent to the Type Traits library as one of the first tests. It turned out to be 20 (msvc, 10 gcc, 4 bcc) times faster, so I expected some speedup. But what I saw yesterday was exceeding all my expectations.
I also see you use inheritance to guide implicit conversions. Clever. :)
Just saying 'integral' means "match whatever integral type" and is a weaker match than 'lvalue<integral>' or 'rvalue<integral>'. Using 'integral_or_enum' is an even weaker match than 'integral' but still stronger than 'object_lvalue', which matches stronger than 'object_lvalue' which in turn is a stronger match than 'object'.... Using multiple inheritance only, would make rule set development very hard and error-prone. This way it's possible to emphasize quite complex rules without running into ambiguity problems. Using an extra argument for disambiguation should _only_ be required when things get extremely tricky, that is the rules _are_ in fact (sematically, that is) ambigous and need prioritization.
It also goes further than the original version, checking for lvalue/rvalue, which might also be useful for concept checking, where these are specified.
This check doesn't cost much - so it was reasonable to have it...
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:
The namespace 'two_way' defines the result type for a rule set of a 'two way filter', having two result states 'filter' and 'pass'. There could be a 'three way filter' with three states like "filter and return true", "filter and return false" and "let the delegate decide" for example or an 'n way filter' with a result type template to carry an integral value... In some "macro-ized" form these could be made interchangable... However, it may change and I am not sure on this, yet. Had to place this somewhere, though - and I did not want to obscure 'has_plus_op.hpp' with negligibilities.
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)
The == operator within an integral constant expression may confuse some compilers, so the comparison is wrapped within a metafunction.
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):
I don't think it's a good idea (see below).
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").
I guess I have to be more precise: The problem is that some compilers do not properly inherit type/template/static-const members. In this case the carrier classes need special care to interoperate nicely with mpl placeholders. The Type Traits library addresses this problem with these def/undef multi-#include headers that define and undefine a set of macros to create the frontend class with all "workaround decorations" needed for the compiler in use. The '_impl' classes do not have to care about it and just publish a 'value' or 'type' member.
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.
There are not too many dependencies left, are there ;+) ? The actual operator tester is very flat. There's nothing to stip in the filter, either. Well, using mpl::and_ is _probably_ breaking a butterfly on a wheel here. On the other hand it's very portable and good readable. I will check on this.
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. :)
By the way - I ran the (naiv) benchmark test on BCC and it worked without having to change anything.
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.
Great ! It will need some refinement in terms of wrapping this into a fine modular design supportive to library development. Any suggestions are welcome, of course ! Regards, Tobias

From: "Tobias Schwinger" <tschwinger@neoscientists.org>
Terje Slettebø wrote:
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. ;)
Beware! It might be Intel (or EDG) is not 100% supported out of the box.
However it's a matter of seconds to find out and likely to be a matter of minutes to adjust it, if necessary.
See the "how to port" part in the "readme.txt".
The basic_test.cpp doesn't work fully on Intel C++ (only 10 of 2500 tests fail, though), and I'll see if I can fix it, using the instructions there.
I refined the 'is_enum' hack I posted (somewhere in this thread) until its behaviour was aequivalent to the Type Traits library as one of the first tests. It turned out to be 20 (msvc, 10 gcc, 4 bcc) times faster, so I expected some speedup.
Wow, nice. :)
But what I saw yesterday was exceeding all my expectations.
Mine, too. :)
I also see you use inheritance to guide implicit conversions. Clever. :)
Just saying 'integral' means "match whatever integral type" and is a weaker match than 'lvalue<integral>' or 'rvalue<integral>'. Using 'integral_or_enum' is an even weaker match than 'integral' but still stronger than 'object_lvalue', which matches stronger than 'object_lvalue' which in turn is a stronger match than 'object'....
Using multiple inheritance only, would make rule set development very hard and error-prone. This way it's possible to emphasize quite complex rules without running into ambiguity problems.
Right.
Using an extra argument for disambiguation should _only_ be required when things get extremely tricky, that is the rules _are_ in fact (sematically, that is) ambigous and need prioritization.
This check doesn't cost much - so it was reasonable to have it...
I'm wondering about the "two_way" part of the code, could you say what
name alludes to, and what code that refers to? Also, regarding this
I see. the part:
The namespace 'two_way' defines the result type for a rule set of a 'two way filter', having two result states 'filter' and 'pass'.
Ah, two outcomes. I was thinking two-way, as in testing that a == b and b == a, that sort of thing. :)
There could be a 'three way filter' with three states like "filter and return true", "filter and return false" and "let the delegate decide" for example or an 'n way filter' with a result type template to carry an integral value...
In some "macro-ized" form these could be made interchangable...
I understand.
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):
I don't think it's a good idea (see below).
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").
I guess I have to be more precise:
The problem is that some compilers do not properly inherit type/template/static-const members. In this case the carrier classes need special care to interoperate nicely with mpl placeholders.
The Type Traits library addresses this problem with these def/undef multi-#include headers that define and undefine a set of macros to create the frontend class with all "workaround decorations" needed for the compiler in use. The '_impl' classes do not have to care about it and just publish a 'value' or 'type' member.
Ok.
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.
There are not too many dependencies left, are there ;+) ?
Nope. :) It's only mpl::and_, actually.
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.
Great !
It will need some refinement in terms of wrapping this into a fine modular design supportive to library development. Any suggestions are welcome, of course !
Indeed. We have a job to do. :) Regards, Terje

Terje Slettebø wrote:
Even if we found a way to give a numerical code or something to each fundamental type (typical typeof-implementation), there's still enum, so we need some kind of is_enum-detection (which would likely require the Boost one).
//// Fast and portable enum detection //// //// Notes: //// - The test facility won't link. //// - Thins usually done with boost libraries were emulated in a //// very simplified form here, in order to test it with the //// online version of the Comeau Compiler //// Known to compile with: //// - Comeau-online //// - gcc 3.2.3 (mingw) //// - bcc 5.4.6 //// - msvc 13.10.3077 // tags 'n tagging types enum { no = 1, yes = 2 }; typedef char (& no_type )[no]; typedef char (& yes_type)[yes]; // is there a standard conversion sequence to int ? struct int_conv_type { int_conv_type(int); }; no_type int_conv_tester (...); yes_type int_conv_tester (int_conv_type); // is this pointer void compatible ? no_type vfnd_ptr_tester (const volatile char *); no_type vfnd_ptr_tester (const volatile short *); no_type vfnd_ptr_tester (const volatile int *); no_type vfnd_ptr_tester (const volatile long *); no_type vfnd_ptr_tester (const volatile double *); no_type vfnd_ptr_tester (const volatile float *); no_type vfnd_ptr_tester (const volatile bool *); yes_type vfnd_ptr_tester (const volatile void *); // strips the reference, adds a pointer template <typename T> T* add_ptr(T&); // converts a bool to yes_type / no_type template <bool C> struct bool_to_yesno { typedef no_type type; }; template <> struct bool_to_yesno<true> { typedef yes_type type; }; // is enum metafunction template <typename T> struct is_enum { private: typedef is_enum self_type; static T & arg; public: // if T is standard-convertible to int and T* is not convertible to // any fundamental type but void, T is an enum. static const bool value = ( (sizeof(int_conv_tester(arg)) == sizeof(yes_type)) && (sizeof(vfnd_ptr_tester(add_ptr(arg))) == sizeof(yes_type)) ); typedef typename bool_to_yesno<self_type::value>::type type; }; // void specializations template <> struct is_enum<void> { typedef no_type type; static const bool value = false; }; template <> struct is_enum<void const> { typedef no_type type; static const bool value = false; }; template <> struct is_enum<void volatile> { typedef no_type type; static const bool value = false; }; template <> struct is_enum<void const volatile> { typedef no_type type; static const bool value = false; }; //////////////////////////////////// // Prove it works... // Test types // enum an_enum { a,b,c }; int i = 0; struct X { void memb(); // try to confuse is_enum with user-defined // implicit conversion operator int(); operator const double*(); operator double(); }; union Y { void memb(); // try to confuse is_enum with user-defined // implicit conversion operator int(); operator const double*(); operator double(); }; void func(); X o; Y u; int & iref (i); float f = 0.0f; void* vptr = &i; char carr[6] = "hello"; char * cstr = carr; double * dptr = (double*) 0L; // Compile time assertion facility // // extract a yes_type / no_type pseudo-instance // from the is_enum metafunction template <typename T> typename is_enum<T>::type func_wrapper(T); // try to initialize a reference to char array with the tag's size with // a yes_type / no_type tagging type as assertion #define ASSERT(ord,expected,expr) \ char (& __assertion##ord##__) [ expected ] = func_wrapper( expr ) // Assert correctness // ASSERT( 1, yes , a); ASSERT( 2, no , i); ASSERT( 3, no , o); ASSERT( 4, no , & X::memb); ASSERT( 5, no , * func); ASSERT( 6, no , func); ASSERT( 7, no , & func); ASSERT( 8, no , iref); ASSERT( 9, no , u); ASSERT(10, no , vptr); ASSERT(11, no , carr); ASSERT(12, no , cstr); ASSERT(13, no , f); ASSERT(14, no , dptr);

From: "Tobias Schwinger" <tschwinger@neoscientists.org>
By the way, nice idea with the member function pointer traits proposal (in the "[type_traits] "member_function_pointer_traits"" thread). The operator/concept traits library contains a similar, but orthogonal component, has_member_function<> (in type_traits_ext/has_member_function.hpp). E.g., to detect a member function with the signature "void A::swap(A &)", one can use: has_member_function<A, has_member_swap, void, A &>::value The parameters are: Class (A), class template for member detection ("has_member_swap", defined using macro in has_member.hpp header), return type (void), any parameters (A &). I think the components are orthogonal, because the above concerns member function _detection_, while your traits are concerned with whether or not a type T is a member function pointer, and if it is, what are the properties of the member function. In other words, if you do is_member_function_pointer<&A::swap>, and A doesn't have a swap() member, you'll get an error, rather than false. I think your proposal would nicely complement the existing Boost type traits.
I had a look at your library proposal today. Looks like a very valuable tool for everyone into generic programming.
Thanks. :)
It seems quite easy to add new concepts, which I think is one of the most important things (the has_member family looks very promising).
I'll expand the docs for that section, as well, as mentioned in a recent posting. I agree that this issue is one of the most important ones.
I was able to build the operator_traits test with GCC 3.2.3 mingw (about 20 minutes PIII,600MHz,512MB RAM - 100% passed - guess I just had to see it with my own eyes ;+).
Ah. :) The std_concept_traits_test.cpp and boost_mpl_concept_traits_test.cpp doesn't work fully on g++ 3.2, yet, but I intend to work on that.
It failed to build on MSVC 13.10.3077 and Borland 5.4.6 (I kept the output, in case you are interested).
Strange, as it works on MSVC 12.00.8804, which is the one I've tested it on (in addition to Intel C++ 7.1 and g++ 3.2). I don't think I have any hope of getting it to work on Borland 5.x (or 6.x, for that matter). I haven't tested it there, and my aim has been Intel C++ 7.1 (maybe 6.0 and up, but I've used 7.1 for testing, so far), MSVC 7.1 and g++ 3.2 (and later for all). Of course, it would be nice if it was possible to get it to work on other compilers, as well. Sure, I'd appreciate it if you could send the compiler output to me.
It might be possible to put the prefiltering stage of the operator traits into sort of table based form, making it easier to maintain, eliminating a lot of inclusion dependencies and template instantiations and covering tests for builtin operators (if they should be detected, that is) as well.
Interesting idea... Something to remove that pre-filtering "boiler plate" code with something less taylor-made for each trait could certainly be useful, and potentially speed up the compilation, as well as removing a lot of inclusion dependencies, yes. However, I haven't been able to come up with anything like that, so far.
This can probably be done with a technique in some way similar to the one applied to the operator checking itself - defining a rule set of overloaded functions to match the exclusions. Classes with a converting copy constructor can be used to categorize the types matched by the rules. The rule sets for different operator categories could be put into nested namespaces so inner categories could 'inherit' the rules of the outter ones. I attached some experimental source code for the prefiltering of an operator+ check to clearify the basic idea (compiles with GCC and MSVC and CVS version of boost).
Great. :) I'll check it out. Regards, Terje

From: "Tobias Schwinger" <tschwinger@neoscientists.org>
I attached some experimental source code for the prefiltering of an operator+ check to clearify the basic idea (compiles with GCC and MSVC and CVS version of boost).
It works indeed with GCC and MSVC, but Intel C++ 7.1 had some serious problems with it. The following is one of several of the same kind of error messages I got: op_test_prefilter_test.cpp(72): error: more than one instance of overloaded function "filter" matches the argument list: function "filter(integral, integral)" function "filter(integral, real)" function "filter(real, integral)" function "filter(real, real)" argument types are: (int, int) BOOST_STATIC_CONSTANT(int, state = ( sizeof(filter(arg1, arg2)) )); ^ detected during instantiation of class "op_test_input_filter<T1, T2, Operation> [with T1=int, T2=int, Operation=always]" at line 162 I haven't had a chance to examine the implementation, yet (but intend to), but maybe the above error could be of help to point out any problem. "BOOST_STATIC_ASSERT(false);" also had to be changed to "BOOST_STATIC_ASSERT((T1 *) 0);", in order to avoid premature instantiation, since "false" is not dependent on the template arguments. Regards, Terje
participants (11)
-
Alex Chovanec
-
Andy Little
-
Daryle Walker
-
David Abrahams
-
Joel
-
John Torjo
-
Jonathan Turkanis
-
Matt Hurd
-
Terje Slettebø
-
Tobias Schwinger
-
Victor A. Wagner Jr.