Boost logo

Boost :

Subject: Re: [boost] C++11 challenge: ASSERT_INVALID_EXPR
From: John Bytheway (jbytheway+boost_at_[hidden])
Date: 2011-12-05 19:49:34


On 04/12/11 17:20, Jeremiah Willcock wrote:
> On Sun, 4 Dec 2011, Dave Abrahams wrote:
>
>>
>> I don't know whether this is actually possible, but I was thinking today
>> that there might be a way to use the new SFINAE to detect errors in a
>> large class of invalid expressions without actually causing a hard
>> error, and thereby build an assertion that fails compilation when passed
>> a valid expression and passes for any invalid expression in the large
>> class mentioned above.
>>
>> Any takers?
>
> Here's the best I've been able to come up with. It works on GCC 4.6 (I
> tested with a prerelease, so the bug I mention might have been fixed).
> It only works within classes, and you need to use a separate template
> parameter in the tested expression (it would be nice if you could shadow
> template parameters in a nested class, BTW).

Both responses to this request so far have given an example where the
expression uses types, rather than values. I think we can do better if
we use values, precisely because we can shadow value names, where we
can't shadow template parameters. Again, I can only make it work in a
class context. For example, the following code can be made to do the
right thing:

template<typename T, typename U>
struct A {
  T x;
  U y;
  ASSERT_INVALID_EXPR((x), x+y, "Can add x and y; bad");
};

int main()
{
  A<char*, char*>();
  A<int, int>(); // generates assertion failure
}

Here the second argument to ASSERT_INVALID_EXPR is the expression, and
the first is a PP_SEQ of some values used in it (enough such that every
sub-expression which might be invalid uses one of the values). So,
instead of "(x)", we could have passed "(x)(y)" here and it would have
worked just as well. The macro in this case expands to something which
is essentially:

  struct invalid_expr_test {
    template<typename T0>
    static boost::mpl::false_ is_ok(int*, T0&& x, decltype(x+y)* = 0);

    template<typename T0>
    static boost::mpl::true_ is_ok(bool, T0&&);

    typedef decltype(is_ok((int*)0, x)) test_type;
  };
  static_assert(invalid_expr_test::test_type::value,
    "Can add x and y; shouldn't be able to");;

By shadowing the specified variable names (in this case x) we make the
expression a type-dependent expression, and avoid the hard error. I
attach the implementation with all the gory preprocessor metaprogramming
details.

Tested with g++ 4.5.2, g++ 4.6.2, and clang++ trunk 143505.

John Bytheway




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