
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