Boost logo

Boost :

Subject: Re: [boost] C++11 challenge: ASSERT_INVALID_EXPR
From: Dave Abrahams (dave_at_[hidden])
Date: 2011-12-23 15:36:06


Hi guys,

Just wanted to say sorry for the radio silence and thanks for making the
effort. These are great solutions but I guess the whole idea is less
interesting than I thought it was when I proposed it. SFINAE is still
pretty limited in what it can detect without causing an error.

on Sun Dec 04 2011, Jeremiah Willcock <jewillco-AT-osl.iu.edu> 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).
>
> -- Jeremiah Willcock
>
> #include <utility>
> #include <boost/preprocessor/cat.hpp>
> #include <boost/mpl/bool.hpp>
>
> template <typename T> struct identity {typedef T type;};
> template <typename U> struct test_template {
> #define BOOST_ASSERT_INVALID_EXPR(tparam, e, msg) \
> struct BOOST_PP_CAT(invalid_expr_test, __LINE__) { \
> template <typename BOOST_PP_CAT(tparam, _), typename = decltype((e))> struct expr_type {typedef void type;}; \
> static boost::mpl::bool_<false> is_ok(bool); \
> template <typename T> static boost::mpl::bool_<true> is_ok(T*, const typename expr_type<T>::type* = 0); \
> }; \
> /* identity here works around parsing bug in GCC 4.6 (decltype(foo)::value doesn't work) */ \
> static_assert(identity<decltype(BOOST_PP_CAT(invalid_expr_test, __LINE__)::is_ok((tparam*)0))>::type::value, msg);
> BOOST_ASSERT_INVALID_EXPR(U, std::declval<U_>() + "bar", "valid");
> };
>
> #if 0
> // This requires the ability to put member templates in local classes
>
> template <typename T>
> void test2(T*) {
> BOOST_ASSERT_INVALID_EXPR(T, "foo" + std::declval<T_>(), "valid2");
> }
> #endif
>
> int main(int, char**) {
> test_template<int> a;
> test_template<char*> b;
> #if 0
> test2((int*)0);
> test2((char**)0);
> #endif
> return 0;
> }
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
>

on Mon Dec 05 2011, John Bytheway <jbytheway+boost-AT-gmail.com> wrote:

> 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
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

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