Boost logo

Boost :

Subject: Re: [boost] RFC Expression Validity Asserts
From: Matt Calabrese (rivorus_at_[hidden])
Date: 2015-01-22 19:09:24


On Thu, Jan 22, 2015 at 3:49 PM, Edward Diener <eldiener_at_[hidden]>
wrote:

> On 1/22/2015 6:05 PM, Matt Calabrese wrote:
>
>> On Wed, Jan 21, 2015 at 4:52 PM, Matt Calabrese <rivorus_at_[hidden]>
>> wrote:
>>
>> I've found it useful, especially in tests, to easily be able to do
>>> expression validity checking via SFINAE in a static assert.
>>>
>>>
>> Bump, and also a couple of additional macros for checking constexpr-ness
>> of
>> an expression are linked farther down in this email, using similar tricks
>> as the SFINAE checks int the previous email (pushing more complicated
>> checks into the body of a lambda such that the macro expands to an
>> expression).
>>
>> Motivation for this is that often when writing templated code it is
>> difficult to be certain that a specific instantiation is actually
>> constexpr
>> even if the constexpr keyword is used with the function template. For
>> instance, for a matrix type whose value type is a template parameter T, it
>> can be difficult to know if matrix<T>'s multiplication operation actually
>> results in a constexpr object, even if it has constexpr inputs.
>> Particularly when designing such a library, it is useful to be able to
>> write simple tests that check the constexpr-ness of a specified expression
>> in the form of a static_assert. A proof-of-concept implementation of this
>> is linked below:
>>
>> http://ideone.com/ko09KQ
>>
>>
>> As an example of such a usage:
>>
>> ////////////////////
>>
>> int foo(int) { return 0; }
>>
>> STATIC_ASSERT_CONSTEXPR
>> (
>> (constexpr int) var = 5,
>> foo(var)
>> );
>>
>> ////////////////////
>>
>> Produces the error:
>>
>> prog.cpp:70:1: error: static assertion failed:
>>
>> ********************
>>
>> Given:
>> constexpr int var = 5;
>>
>> The following expression was expected to be constexpr but was not:
>> foo(var)
>>
>> ********************
>>
>
> This looks interesting but I am confused about where this implementation
> is. Is it available somewhere ? is it documented at all ? Are there
> examples/tests ? Is there a good general of when some of these macros
> should/would be used in template code ?
>

Slimmed-down implementations were linked in the emails (this is just a
request for comments, I'm not proposing anything -- more just seeing if
people find this useful so that I can consider proposing a more capable
boosty implementation):

SFINAE checks: http://ideone.com/RHd6FM
constexpr checks: http://ideone.com/ko09KQ

The examples are self-contained and able to be compiled/run in the browser
if you want to play around with them. Both examples have example uses at
the bottom of the file. I apologize that they are not documented --
consider them just as a proof of concept to show that the macros work.
Describing how they work is a bit complicated and I didn't feel like going
into details. If any of the magic needs clarification, let me know.

For details on how to invoke the macros linked, the basic pattern for the
SFINAE checks are:

STATIC_ASSERT_VALID
(
    (int) a, // This says "given a variable a of type int..."
    ++a // Would the expression ++a be "valid"
);

The "invalid" expression check works similarly. Underneath the hood is an
IS_VALID_EXPR macro that has the same form and is usable on its own,
yielding a constexpr bool expression. For instance, you can use it to
easily do:

template <class T>
struct has_preincrement_operator
    : std::integral_constant<bool, IS_VALID_EXPR((T) object, ++a)> {};

The above checks that T supports ++a, without the user having to do SFINAE
directly (it also does not place any other requirements on T, other than it
not being a void type or an incomplete reference type, and probably a
couple of other oddball cases, but those can all be handled gracefully with
a slightly more complicated implementation).

The constexpr macros work in the same manner, only you need to initialize
your "given" objects.
Another limitation of the constexpr facility is that your givens must all
be constexpr if you reference them in the expression that you are checking.
It also will produce a compile-time error instead of a nice static_assert
if the expression yields void (there might be some magic to fix, but I
haven't given it much thought). It will also fail if your expression is
simply the name of a global object (there also might be a way around this).

It looks like you are saying that in place of BOOST_STATIC_ASSERT one can
> use your macros at compile time to produce compile time errors, but the
> majority of C++ expressions have to be evaluated at run-time. So is this
> strictly for compile-time expressions or what ?

These are strictly compile-time checks. I find them particularly useful for
concept checking and for writing unit tests.

> Your original OP looked like it worked with run-time expressions ( ++a,
> a+b etc. ).
>

It is doing expression-validity checking (using C++11 extended SFINAE
rules). The ++a in the example email is checking if ++a would be valid if
"a" were of the specified type during substitution.

A more boostified version which I have implemented locally allows the
SFINAE checks to work with any number of "given" declarations and any
number of expressions. It only depends on Boost.Preprocessor.

-- 
-Matt Calabrese

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