
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