I would like to express a static_assert with the form:
static_assert(expression should not compile);
Let me add a complete example:
template <bool Big>
struct A{};
template <>
struct A<true>
{
void a() {}
};
A<false> b;
static_assert(!compile(b.a()));
or
static_assert(!compile(A<false>::a()));
So, the idea is to be able to ensure that an expression (with valid syntax) will not compile.
It will be better if the solution only uses C++11 if possible.
If the condition is true, the static_assert declaration has no effect. If the condition is false, the assertion fails, the compiler displays the message in string_literal parameter and the compilation fails with an error.
static_assert is a keyword defined in the <assert. h> header. It is available in the C11 version of C. static_assert is used to ensure that a condition is true when the code is compiled.
Use static_assert for assertions that should not occur on a usual basis.
OK, given the context of your question is somewhat vague, this answer might not be appropriate for your case. However, I found this a very interesting challenge.
Clearly, as stated in the comments, the solution will have to exploit some kind of (expression) SFINAE. Basically, what we need is a more generic variant of the detection idiom. Hovewer, there are mainly two problems here:
1) To make SFINAE kick in, we need some kind of templates.
2) To provide the compile(XXX)
syntax, we need to create these templates "on the fly" inside a macro. Otherwise we would have to define a test function for each test in advance.
The second constraint makes things rather difficult. We can define structs and functions locally inside lambdas. Unfortunately, templates are not allowed there.
So here is, how far I was able to get (not 100% what you want, but relatively close).
Usually, the expression-SFINAE-detectors leverage either (template) function overloading or class template specialisation. As both are not allowed inside a lambda, we need an additional layer: a functor that takes a bunch of lambdas and calls the one that fits the call arguments best. This is often used in combination with std::variant
.
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Now we can create a detector like this:
auto detector = overloaded{
[](auto, auto) -> std::false_type {return {};}
,
[](auto x, int)-> decltype(decltype(x)::a(), std::true_type{}){ return {};}
};
static_assert(!detector(A<false>{}, int{}));
static_assert(detector(A<true>{}, int{}));
Now, we can define a macro, that defines and calls a dector for the desired expression:
#define compile(obj, xpr) \
[]() { \
auto check = \
overloaded{[](auto&&, auto) -> std::false_type { return {}; }, \
[](auto&& x, int) -> decltype(x xpr, std::true_type{}) { \
return {}; \
}}; \
return decltype(check(obj, int{})){}; \
}()
This macro creates a lambda, substitutes the xpr
into the detector, and performs type deduction on decltype(x)
to make SFINAE kick in. It can be used as follows:
static_assert(!compile(b, .a()));
static_assert(compile(a, .a()));
int x = 0;
static_assert(compile(x, *= 5));
static_assert(!compile(x, *= "blah"));
Unfortunately, it won't work with a typename as first argument. Therefore we need a second macro for those kinds af tests:
#define compile_static(clazz, xpr) \
[]() { \
auto check = overloaded{ \
[](auto, auto) -> std::false_type { return {}; }, \
[](auto x, int) -> decltype(decltype(x) xpr, std::true_type{}) { \
return {}; \
}}; \
return decltype(check(std::declval<clazz>(), int{})){}; \
}()
static_assert(!compile_static(A<false>, ::a()));
static_assert(compile_static(A<true>, ::a()));
As stated above, this is not 100% what you requested, as we'll always need an additional ,
to separate the macro arguments. Also, it needs two separate macros. Maybe this is something that can be impoved using the preprocessor to detect whether the xpr
argument starts with ::
. And, of course, there might be cases, where it doesn't work. but perhaps it is a starting point.
It requires c++17, but seems to work with gcc >= 7, clang >= 5 and even msvc 19.
Here is a full example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With