Boost logo

Boost :

Subject: Re: [boost] [contract] SFINAE question for concepts
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2012-10-03 21:49:14


On Wed, Oct 3, 2012 at 4:41 PM, Matt Calabrese <rivorus_at_[hidden]> wrote:
> On Wed, Oct 3, 2012 at 4:02 PM, Lorenzo Caminiti <lorcaminiti_at_[hidden]>wrote:
>
>> Hello all,
>>
>> I have a SFINAE question. The following correctly returns false for
>> eq1<x>::value when x has no operator==
>
>
> I encountered the same problem in GCC when developing Generic, only with
> constructor-style initialization at the time -- I thought it was just a
> compiler bug, but since Clang is doing it too, maybe it's not. What I
> assumed was happening was that since it's in an unevaluated context, the
> compiler realized it could get the sizeof the expression without needing to
> know anything about the type of the subexpression, so it [incorrectly] only
> did syntax checking on it. In other words, it short-circuits before doing
> substitution so SFINAE never rules it out.

I noticed that wrapping the bool{} in a decltype works*:

#define REQUIRES_T_a_b_c(n, expr) \
    struct BOOST_PP_CAT(requires, n) \
    { \
        T a; T b; T c; \
        template< typename TX > static boost::yes_type check ( \
            TX a, TX b, char(*)[sizeof \
                (decltype(expr)) \
            ] \
        ); \
        template< typename TX > static boost::no_type check ( ... ); \
        static const bool value = sizeof(boost::yes_type) == \
                sizeof check<T>(a, b, 0); \
    };

template< typename T >
struct EqualityComparable
{
    REQUIRES_T_a_b_c(0, bool{a == b})
    REQUIRES_T_a_b_c(1, bool{a != b})
    static const bool value = requires0::value && requires1::value;
};

But I still would like to understand why it doesn't work without the decltype...

(*) I'm just playing around now with some example so "works" has a
very limited meaning at the moment.

This way, I should be able to handle expressions... for types, I'm
thinking require a typename prefix in the concept declaration syntax
so I know not to use the decltype() in the code expanded by the
macros:

CONTRACT_CONCEPT(
    concept (Common) ( typename T, typename U ) (
        requires(
            typename (CommonType<T, U>) // typename so I know this is
a type and not an expression and expanded code won't use decltype
        )
    )
)

> However, I'm testing it now when
> not inside of a place where SFINAE may occur (though still with template
> arguments), and I correctly get an error when the template is instantiated.
> This is true with both GCC and Clang, so it seems like this unintuitive
> behavior may, in fact, be standard. I don't have time to check it out right
> now, but if someone references the standard and still can't figure out why
> you're getting this behavior, you could always submit a bug report to
> Clang. They're versed enough in standardese to determine if it's standard
> behavior or not.
>
> I'd say that you could use a workaround, but I assume you want the {}
> syntax so that you could do the N3351 syntax for expression validation
> directly.
>
> Also, I'm going to warn you now because I went down this same exact path
> with Generic -- you're going to have to change your approach for
> representing givens -- datamembers and even function parameters will not
> work. The problem is, you need to account for checking of "void", but with
> this implementation, you'll get a hard error instead of a compile-time bool
> value. You need to get everything into a context where SFINAE can take
> place. My approach with the old-style generic was to do all of the checking
> in a template specialization pattern list (as opposed to in a function
> template definition), and I used template reference parameters for the
> givens.

Yep, I'm not sure about data/function members... I'll definitely look
at Boost.Generic source!

Thanks,
--Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk