RFC Expression Validity Asserts

I've found it useful, especially in tests, to easily be able to do expression validity checking via SFINAE in a static assert. In other words, it's frequently useful to want to quickly test to make sure that a particular expression does or does not compile, similar in some ways to a concepts-lite usage pattern. The general idea is that you want to static assert that, given a series of object declarations of a specific type, a specified expression or series of expressions that deal with those objects would or would not cause substitution to fail had the object types been dependent on a template parameter. It internally relies on a macro that is invoked as such: ////////// IS_VALID_EXPR( (int) a, (float) b, // etc.. (a + b) (a - b) // etc. ) ////////// The above macro invocation results in an expression that is a constexpr bool which is true iff substitution would not fail. With the above macro, one can do, for instance: static_assert(IS_VALID_EXPR((const int) a, ++a), "Some error message."); I've found this pattern so common that I've created a macro that takes such a series of declarations and expressions and automatically produces a simple static_assert, outputting the declarations and the expression that did not compile. A slimmed down version of this macro can be toyed with via the link below (this slim version allows only one declaration and one expression per macro): http://ideone.com/RHd6FM Briefly, using the macro as such: STATIC_ASSERT_VALID ( (const int) a, ++a ); is able to produce a very human-readable error message: prog.cpp:58:1: error: static assertion failed: ******************** Given: const int a; The following expression was expected to compile but did not: ++a ******************** There is a similar macro for verifying that an expression does not compile, which I find is much more useful in practice. Notable is that the validity macro is an expression -- no statement or other declarations are introduced -- and the static assert macro produces a single static_assert. This means that you can use the assertion macro in the normal scopes that any static_assert can be used (namespace scope, class scope, function scope...). One limitation is that because it uses lambdas behind the scenes, the context in which the validity macro may be used is slightly restricted (I.E. it can't be used in an un-evaluated context). Is this something people would generally find useful? If it is considered generally useful, I don't think it's really worthy of its own library, so where would it go? Years ago I probably would have just assumed utility, but I'm not really sure where it would go in the modern boost structure. The slim version can easily have no dependencies, though the more general solution ideally depends on Boost.Preprocessor. The macros themselves require C++14, though slightly less-useful forms (ones that introduce additional declarations) could be made that would work in C++11. -- -Matt Calabrese

On Wed, Jan 21, 2015 at 4:52 PM, Matt Calabrese
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) ******************** - Matt Calabrese

On 1/22/2015 6:05 PM, Matt Calabrese wrote:
On Wed, Jan 21, 2015 at 4:52 PM, Matt Calabrese
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:
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 ? 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 ? Your original OP looked like it worked with run-time expressions ( ++a, a+b etc. ).

On Thu, Jan 22, 2015 at 3:49 PM, Edward Diener
On 1/22/2015 6:05 PM, Matt Calabrese wrote:
On Wed, Jan 21, 2015 at 4:52 PM, Matt Calabrese
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:
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
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

On Thu, Jan 22, 2015 at 4:12 PM, Matt Calabrese
On Thu, Jan 22, 2015 at 4:09 PM, Matt Calabrese
wrote: For instance, you can use it to easily do:
template <class T> struct has_preincrement_operator : std::integral_constant
{};
Hmm, actually this use doesn't seem to work, though I'm not sure why... I should have tested it in the browser before posting the sample. I'm pretty sure it fails due to a compile-bug but I'm not entirely certain. -- -Matt Calabrese

2015-01-23 8:26 GMT+08:00 Matt Calabrese
On Thu, Jan 22, 2015 at 4:12 PM, Matt Calabrese
wrote: On Thu, Jan 22, 2015 at 4:09 PM, Matt Calabrese
wrote: For instance, you can use it to easily do:
template <class T> struct has_preincrement_operator : std::integral_constant
{}; Hmm, actually this use doesn't seem to work, though I'm not sure why... I should have tested it in the browser before posting the sample. I'm pretty sure it fails due to a compile-bug but I'm not entirely certain.
I'm sure that lambda cannot be used in unevaluated context.

On Thu, Jan 22, 2015 at 5:24 PM, TONGARI J
2015-01-23 8:26 GMT+08:00 Matt Calabrese
: On Thu, Jan 22, 2015 at 4:12 PM, Matt Calabrese
wrote: On Thu, Jan 22, 2015 at 4:09 PM, Matt Calabrese
wrote: For instance, you can use it to easily do:
template <class T> struct has_preincrement_operator : std::integral_constant
{}; Hmm, actually this use doesn't seem to work, though I'm not sure why... I should have tested it in the browser before posting the sample. I'm pretty sure it fails due to a compile-bug but I'm not entirely certain.
I'm sure that lambda cannot be used in unevaluated context.
Right, but the magic of this trick is that it's not actually using the
lambda in an unevaluated context (at least that's my understanding, and
I've searched the standard for all instances of the term unevaluated)! A
lambda itself never actually appears in a
typeid/decltype/sizeof/alignof/noexcept. I do the tricks indirectly, using
the lambdas just as a means to introduce declarations inside a nested scope
of the expression and to create nested templates via generic lambdas for
SFINAE. No lambda itself is actually invoked or directly used in an
unevaluated context. I deduce information that the lambdas contain
indirectly.
Anyway, what is interesting is that the following *does* compile, as I
thought it would:
//////////
using T = std::integral_constant

2015-01-23 10:11 GMT+08:00 Matt Calabrese
On Thu, Jan 22, 2015 at 5:24 PM, TONGARI J
wrote: 2015-01-23 8:26 GMT+08:00 Matt Calabrese
: On Thu, Jan 22, 2015 at 4:12 PM, Matt Calabrese
wrote: On Thu, Jan 22, 2015 at 4:09 PM, Matt Calabrese
wrote: For instance, you can use it to easily do:
template <class T> struct has_preincrement_operator : std::integral_constant
{}; Hmm, actually this use doesn't seem to work, though I'm not sure why... I should have tested it in the browser before posting the sample. I'm pretty sure it fails due to a compile-bug but I'm not entirely certain.
I'm sure that lambda cannot be used in unevaluated context.
Right, but the magic of this trick is that it's not actually using the lambda in an unevaluated context (at least that's my understanding, and I've searched the standard for all instances of the term unevaluated)! A lambda itself never actually appears in a typeid/decltype/sizeof/alignof/noexcept. I do the tricks indirectly, using the lambdas just as a means to introduce declarations inside a nested scope of the expression and to create nested templates via generic lambdas for SFINAE. No lambda itself is actually invoked or directly used in an unevaluated context. I deduce information that the lambdas contain indirectly.
Anyway, what is interesting is that the following *does* compile, as I thought it would:
////////// using T = std::integral_constant
; ////////// The above uses the lambda internally in the same way as the previous email, but without failure. I just pass the result of the macro invocation as a compile-time bool template argument, with the main difference being that I use "int" here instead of a dependent type. I don't immediately see why that difference should cause failure. As far as I can tell, they should both compile, or if I'm wrong somewhere, it seems that NEITHER should compile.
You're right, I misunderstood the situation. Your code doesn't work for clang, it seems to be more strict than g++: "error: a lambda expression may not appear inside of a constant expression" I suspect it's also a bug, since a hand-written functor does compile.

On Thu, Jan 22, 2015 at 6:49 PM, TONGARI J
You're right, I misunderstood the situation.
Your code doesn't work for clang, it seems to be more strict than g++: "error: a lambda expression may not appear inside of a constant expression" I suspect it's also a bug, since a hand-written functor does compile.
Hmm, this might be the problem, but I'm still not sure if this is a compiler bug or not. According to the standard, the type of the object yielded by a lambda expression has an implicitly declared default constructor/copy constructor/destructor as they are for any other type, and since the type is stateless, that would mean that those operations are constexpr. I don't mind that operator() isn't constexpr because I never actually use it as such. I suppose that if someone were to be pedantic, they could argue that even though all of the constructors are constexpr, the object still might not necessarily be a constant expression. Still, I'm not certain that the omission of such a detail would imply that the object isn't a constant expression. I think the standard may have just unintentionally left this unspecified, in which case it would be valid for a compiler to accept this code or for a compiler to deny the code. -- -Matt Calabrese

2015-01-23 11:16 GMT+08:00 Matt Calabrese
On Thu, Jan 22, 2015 at 6:49 PM, TONGARI J
wrote: You're right, I misunderstood the situation.
Your code doesn't work for clang, it seems to be more strict than g++: "error: a lambda expression may not appear inside of a constant expression" I suspect it's also a bug, since a hand-written functor does compile.
Hmm, this might be the problem, but I'm still not sure if this is a compiler bug or not. According to the standard, the type of the object yielded by a lambda expression has an implicitly declared default constructor/copy constructor/destructor as they are for any other type, and since the type is stateless, that would mean that those operations are constexpr. I don't mind that operator() isn't constexpr because I never actually use it as such.
I suppose that if someone were to be pedantic, they could argue that even though all of the constructors are constexpr, the object still might not necessarily be a constant expression. Still, I'm not certain that the omission of such a detail would imply that the object isn't a constant expression. I think the standard may have just unintentionally left this unspecified, in which case it would be valid for a compiler to accept this code or for a compiler to deny the code.
Not sure if this helps: http://pfultz2.com/blog/2014/09/02/static-lambda/

On Thu, Jan 22, 2015 at 8:05 PM, TONGARI J
Not sure if this helps: http://pfultz2.com/blog/2014/09/02/static-lambda/
I believe that will actually do the trick! -- -Matt Calabrese

According to the standard, the type of the object yielded by a lambda expression has an implicitly declared default constructor/copy constructor/destructor
I believe that a lambda never has a default constructor even for stateless lambdas.(Even though, it should). Paul -- View this message in context: http://boost.2283326.n4.nabble.com/RFC-Expression-Validity-Asserts-tp4671480... Sent from the Boost - Dev mailing list archive at Nabble.com.

Hi, this library might be very useful in a library I'm involved in. Particularly we've developed an interface for linear programing. In the implementation we needed to ensure that this expression compiles: Column x , y; 0 <= x + y <= 10; And this: 0 <= x + y >= 10; does not. We have very ugly solution now and I guess this is a perfect use case for this library. Regards, Piotr

On Sun, Jan 25, 2015 at 5:19 AM, Piotr Wygocki
Hi, this library might be very useful in a library I'm involved in. Particularly we've developed an interface for linear programing. In the implementation we needed to ensure that this expression compiles: Column x , y; 0 <= x + y <= 10;
And this:
0 <= x + y >= 10;
does not.
We have very ugly solution now and I guess this is a perfect use case for this library.
Yeah, that would be trivial to check with this. Unfortunately, I can't seem to get it to work consistently across compilers or even consistently within a single compiler. The most compelling thing about this, IMO, is that because it is itself an expression and doesn't require separate statements, it would be usable directly with enable_if, so if it can be made to work portably you could, for example, write a function template that is enabled if a certain operation is *not* supported (or if a certain operation is or is not constexpr), which without a trick like this requires at least another declaration to perform the SFINAE magic with a separate metafunction. With this, everything would be able to be done entirely as a part of the function template declaration itself. -- -Matt Calabrese
participants (5)
-
Edward Diener
-
Matt Calabrese
-
pfultz2
-
Piotr Wygocki
-
TONGARI J