tr1::reference_wrapper, sfinae and result_type definition

Discussion in 'C++' started by Emmanuel Deloget, Nov 29, 2006.

  1. Hello,

    I'm trying to find a (sfinae powered) way to verify if a particular
    type declares a subtype (either using typedef or by declaring a
    subclass). To be more concrete, let's say that I'm trying to see how to
    implement a tiny part of tr1::reference_wrapper, more exactly the
    definition of its weak result type.

    Let's imagine that class A and B inherit both std::unary_function and
    std::binary_function. As such, they also inherit
    unary_function::result_type and binary_function::result_type.
    reference_wrapper<A> and reference_wrapper<B> shall now define their
    weak result type.

    Let's use these definitions:

    ------------8<----------------------------------------

    struct A :
    unary_function<int, int>,
    binary_function<float, float, float>
    {
    typedef bool result_type;
    };

    struct B :
    unary_function<int, int>,
    binary_function<float, float, float>
    { };

    ------------>8----------------------------------------

    A has in fact 3 different result_type (A::unary_function::result_type,
    A::binary_function::result_type and A::result_type). The TR1 tells me
    that I should define reference_wrapper<A>::result_type as
    A::result_type (quite logical).

    The problem comes if I have to consider B, as I find the wording of the
    TR1 quite obscure in this case. As I understand it, I have two
    possibilities:

    1) either I don't define reference_wrapper<B>::result_type, because B
    doesn't define result_type (although it inherit a result_type member
    from both unary_function and binary_function)

    2) or I define reference_wrapper<B>::result_type as either
    B::unary_function::result_type or B::binary_function::result_type
    (which doesn't make much sense, since these types are different. So
    maybe I should only define reference_wrapper<B>::result_type if
    B::unary_function::result_type and B::binary_function::result_type are
    the same).

    But that's not only the sole problem in this area. Another one comes if
    I want to verify if a class defines a result_type subtype. Consequently
    to the C++ name lookup scheme, if result_type is not defined in a class
    C but is defined (and accessible) in a super class, then C::result_type
    is known and can hardly be differentiated from a subtype of C.

    It means that I cannot differentiate between the cases of class A and
    class B above - I have no way to find that result_type is not defined
    in class B but only in its superclass. The problem is that since B has
    two superclass that define their own result_type, B::result_type
    results in a compile time ambiguousity - the compiler shoke (and he's
    quite right).

    Until now, I used this code (inherited from boost, I think) to try to
    see if a type was defined in a class:

    ------------8<----------------------------------------

    template <class t> class has_result_type
    {
    template <class u> struct wrapper { }
    typedef char one[1];
    typedef char two[2];
    template <class u> static one& test(wrapper<u::result_type>*);
    template <class u> static two& test(...);
    public:
    static const bool value = sizeof(test<t>(0)) == sizeof(one);
    };

    ------------>8----------------------------------------

    Gnu's g++ compiler is quite happy with this code -
    has_result_type<A>::value is true, and has_result_type<B>::value is
    false. MS VC++.NET 2005 compiler is a lot more problematic: both
    has_result_type<A>::value and has_result_type<B>::value are true -
    which is nearly logical, since B really has (at least) a definition of
    result_type. Finally, the Comeau C++ compiler shoke - because
    u::result_type is ambiguous when u = B (this is probably the most
    logical behavior, as I understand how the standard defines name
    lookup).

    As a consequence, either this code is nonportable or it is just non
    standard compliant.

    Which means, in turn, that I still have no way to differentiate between
    class A and class B using a generic way.

    So far, I have two questions:
    1) what is the correct, official intrepretation of the weak result type
    definition in the TR1? (I personnally feel that the wording of this
    definition should be changed to be clearer)
    2) regardless of this interpretation, how can I differientiate between
    the two cases I presented (class A and class B)?

    This issue has bugged be for two weeks now, and I'm not able to devise
    a solution to this problem.

    Thanks for your time, and happy answering! :)

    -- Emmanuel Deloget, Artware
     
    Emmanuel Deloget, Nov 29, 2006
    #1
    1. Advertising

  2. Emmanuel Deloget

    Pete Becker Guest

    Emmanuel Deloget wrote:
    >
    > Let's use these definitions:
    >
    > ------------8<----------------------------------------
    >
    > struct A :
    > unary_function<int, int>,
    > binary_function<float, float, float>
    > {
    > typedef bool result_type;
    > };
    >
    > struct B :
    > unary_function<int, int>,
    > binary_function<float, float, float>
    > { };
    >
    > ------------>8----------------------------------------
    >
    > A has in fact 3 different result_type (A::unary_function::result_type,
    > A::binary_function::result_type and A::result_type). The TR1 tells me
    > that I should define reference_wrapper<A>::result_type as
    > A::result_type (quite logical).
    >


    Not really. A and B should each be derived from either unary_function or
    from binary_function, but not both. Keep in mind that the requirements
    for defining result_type are for backward compatibility with today's
    callable types and with today's compilers. Callable types in the current
    standard have one function call operator, so when you write that type
    you know exactly how many arguments that function call operator takes,
    so you can choose unary_function or binary_function as appropriate. (See
    chapter 6 of my book, "The Standard C++ Library Extensions", for more
    details).

    struct A : unary_function<int, int>
    {
    int operator()(int);
    };

    or

    struct A : binary_function<float, float, float>
    {
    float operator()(float, float);
    };

    If you're trying to do something more sophisticated with multiple
    function call operators and different numbers of arguments you've got
    problems: the support isn't quite there today. Eventually you'll be able
    to do something like this:

    struct A
    {
    int operator()(int);
    float operator()(float);
    };

    A a;
    reference_wrapper<A> awrap = a;
    a(3);
    a(4.5);

    But at present, reference_wrapper isn't able to figure out the return
    type of that function call operation. It relies on result_of, which, for
    now, relies on finding result_type in the type that you're using. And,
    as you've mentioned, it's hard to have more than one result_type in a class.

    --

    -- Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com)
    Author of "The Standard C++ Library Extensions: a Tutorial and
    Reference." (www.petebecker.com/tr1book)
     
    Pete Becker, Nov 29, 2006
    #2
    1. Advertising

  3. Pete Becker a écrit :

    > Not really. A and B should each be derived from either unary_function or
    > from binary_function, but not both. Keep in mind that the requirements
    > for defining result_type are for backward compatibility with today's
    > callable types and with today's compilers. Callable types in the current
    > standard have one function call operator, so when you write that type
    > you know exactly how many arguments that function call operator takes,
    > so you can choose unary_function or binary_function as appropriate. (See
    > chapter 6 of my book, "The Standard C++ Library Extensions", for more
    > details).
    >
    > [snip-a-lot]
    >
    > If you're trying to do something more sophisticated with multiple
    > function call operators and different numbers of arguments you've got
    > problems: the support isn't quite there today. Eventually you'll be able
    > to do something like this:
    >
    > struct A
    > {
    > int operator()(int);
    > float operator()(float);
    > };
    >
    > A a;
    > reference_wrapper<A> awrap = a;
    > a(3);
    > a(4.5);
    >
    > But at present, reference_wrapper isn't able to figure out the return
    > type of that function call operation. It relies on result_of, which, for
    > now, relies on finding result_type in the type that you're using. And,
    > as you've mentioned, it's hard to have more than one result_type in a class.
    >
    > --
    >
    > -- Pete
    > Roundhouse Consulting, Ltd. (www.versatilecoding.com)
    > Author of "The Standard C++ Library Extensions: a Tutorial and
    > Reference." (www.petebecker.com/tr1book)


    First, thanks for you answer.

    Second, it appears that if the class could inherit either
    unary_function or binary_function (but not both) to be
    reference_wrapped, then the TR1 (or at least the TR1 draft that can be
    downloaded from the WG21 website) is not adequately worded (no offense
    intended), as its words implies that both should be supported: 2.1.2
    §3 and 2.1.2 §4 are not mutually exclusive, resulting in your
    servitor trying to implement the union of both clauses. It would also
    simplify things a lot (at least, I'd be able to code this ^_^) ( the
    new C++0x working draft also use a similar wording BTW, in which mutual
    exclusivity is not implied).

    I guess I can now move on some another annoying problem :)

    Regards,

    -- Emmanuel Deloget, Artware
     
    Emmanuel Deloget, Nov 29, 2006
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Emmanuel Deloget
    Replies:
    3
    Views:
    404
    Chris Thomasson
    Mar 3, 2007
  2. Fei Liu
    Replies:
    5
    Views:
    481
    Barry
    May 29, 2008
  3. Felipe Farinon

    reference_wrapper rationale

    Felipe Farinon, Nov 11, 2008, in forum: C++
    Replies:
    1
    Views:
    455
    Maxim Yegorushkin
    Nov 12, 2008
  4. greek_bill

    SFINAE and explicit instantiation

    greek_bill, Nov 21, 2008, in forum: C++
    Replies:
    3
    Views:
    622
    greek_bill
    Nov 26, 2008
  5. Marc
    Replies:
    7
    Views:
    539
Loading...

Share This Page