C++ knows assert() which allows runtime checks that compile to nothing in dependence of NDEBUG.
I would like to replace that macro using compiler-code and avoiding the preprocessor. I need to do the following:
Break or terminate if expressions evaluates to false
Log the code line where the assert is called
Discard the check and the passed expression for NDEBUG builds
Breaking/terminating the application is easy.
In C++20 there is std::experimental::source_location which I can use to get the code location of the assertion.
A compile time conditional could be done using requires or constexpr if
However I do not know how I could avoid the evaluation of the expression. When implementing myAssert(expression) as a function, I need to pass the expression result as a function argument which means it is evaluated anyway, even if the parameter is not used inside the function.
Is there a way to solve this in C++20?
EDIT: A templated example:
template <typename T> requires (gDebug)
void assertTrue(const T& pResult, const std::experimental::source_location& pLocation) noexcept
{
if (!static_cast<bool>(pResult))
{
// error handling
}
}
template <typename T> requires (!gDebug)
void assertTrue(const T&) noexcept
{
}
I suppose you are talking about the case when you disabled debugging and you want the function to be a noop. I see 2 options:
You can use a macro. Macros can be misused, but they have their place and "passing an expression" without evaluating it is a case for a macro.
Alternatively, pass a callable that returns the result you want to assert for. Only call it when gDebug == True:
template <typename F> requires (gDebug)
void assertTrue(const F& f, const std::experimental::source_location& pLocation) noexcept
{
if (!static_cast<bool>(f()))
{
// error handling
}
}
Though this will make the call rather verbose. For example one that fails always:
assertTrue( [](){ return false; });
Related
I am trying to use SFINAE to disable certain functions of a class based on some non-templated enum arguments.
The following code does NOT compile with gcc, but appears to compile and work as expected when using the msvc compiler.
#include <iostream>
#include <type_traits>
enum class B { VARIANT1, VARIANT2 };
template<B B_VAL>
struct A {
template<class = std::enable_if_t<B_VAL == B::VARIANT1>>
void func1() {
std::cout<<"VARIANT1"<<std::endl;
}
template<class = std::enable_if_t<B_VAL == B::VARIANT2>>
void func2() {
std::cout<<"VARIANT2"<<std::endl;
}
};
int main()
{
A<B::VARIANT1> a;
a.func1();
}
The expected (and msvcs) behavior is that calling a function whose enable_if_t condition equates to false results in a compile time error, or the removal of the function candidate for overload resolution if an overloaded function was present in the example. In all other cases, the code should compile normally.
gcc on the other hand tells me that it can't find a type named "type" in "struct std::enable_if<false, void>" for the enable_if_t in the template of func2, which makes perfect sense as the member named "type" is only present in enable_if if the condition equates to true. But shouldn't this be the desired behavior for the SFINAE functionality and shouldn't the compiler ignore func2, as it is never called?
I now have three questions:
As the two compilers produce different behavior, is it undefined and if yes, which parts/statements?
Is SFINAE suited to achieve my goal, or have I misunderstood its use case?
Would I be better off by using static asserts as alternative?
I am sorry if this question is a duplicate of this one, but I don't think that the answers there provided much help with my problem.
GCC is right. And it's because you aren't using SFINAE for your function. It may appear that you do because you employ utilities for SFINAE from the standard library, but there is a crucial ingredient missing here.
The 'S' in "SFINAE" stands for substitution. The substitution of template arguments into the parameters of a template we are trying to instantiate. Now, the template in question is func2. And for SFINAE to work, it is func2's argument that must fail to be substituted for its parameters. But here
std::enable_if_t<B_VAL == B::VARIANT2>
There is no parameter of func2 in use. It doesn't depend on anything that happens during substitution into func2. It's just an invalid type, completely independent of an attempt to actually instantiate func2.
It's not hard to fix though
template<B B_VAL_ = B_VAL, class = std::enable_if_t<B_VAL_ == B::VARIANT1>>
void func1() {
std::cout<<"VARIANT1"<<std::endl;
}
template<B B_VAL_ = B_VAL, class = std::enable_if_t<B_VAL_ == B::VARIANT2>>
void func2() {
std::cout<<"VARIANT2"<<std::endl;
}
Now, the check is against the substitution into the correct template.
I was searching for reasons that prevent my compiler (intel 15) to vectorize my code. The reasons were : loop with early exits cannot be vectorized unless it meets search loop idiom criteria although I did not have any thing like break or exit(0) in it.
So I checked all my function with the noexcept operator and it seems that all my template function are likely to throw exception (the operator returns false all the time) althgough I have no try/catch/throw in it.
For this reason, I specify the noexcept on my function and now it works but I don't understand why the compiler say that they are likely to throw something ? How can I specify that, by default, they won't throw anything ??
Example:
/*.h*/
template <typename T>
void A(T& a){} /*does nothing*/
/*.cpp*/
#include ".h"
#include "MyClass.h"
int main(){
MyClass C;
noexcept(A(C)); /*Return false !!*/
}
Thanks.
If you look at the documentation for the noexcept operator (emphasis mine)
The result is false if the expression contains at least one of the following potentially evaluated constructs:
call to any type of function that does not have non-throwing exception specification, unless it is a constant expression.
throw expression.
dynamic_cast expression when the target type is a reference type, and conversion needs a run time check
typeid expression when argument type is polymorphic class type
In all other cases the result is true.
So as you noticed, the way to have your template function evaluate noexcept as true is simply to declare it as such
template <typename T>
void A(T& a) noexcept
{
}
So I'm trying to restrict operations on a type that has a conversion operator to bool - for example:
template <typename R>
Result
operator&&(const R&) {
STATIC_ASSERT(false, MY_MESSAGE);
return Result();
}
STATIC_ASSERT is my wrapper macro around c++11 static_assert and a macro-ish c++98 static assert.
I want a somewhat useful message as an error for users that try to use this so making it private or deleting it in c++11 aren't an option.
This however works only for MSVC because of the difference between msvc and g++/clang - with g++/clang the static assert always fires - even when the "deleted" function is not used.
The only thing I've seen that would do the trick is to use an undefined type with it's name as the message as the return type of the template - like this:
template<typename R>
STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison&
operator&&(const R&);
I first saw this here
Is there any other way to do this in c++98 - a deleted function with a custom message when the user tries to use it?
in static_assert(false, message), false is non dependent of template.
You have to make your condition depending of template.
as static_assert(!std::is_same<T, T>::value, message)
In short:
How to write a test, that checks that my class is not copyable or copy-assignable, but is only moveable and move-assignable?
In general:
How to write a test, that makes sure that a specific code does not compile? Like this:
// Movable, but non-copyable class
struct A
{
A(const A&) = delete;
A(A&&) {}
};
void DoCopy()
{
A a1;
A a2 = a1;
}
void DoMove()
{
A a1;
A a2 = std::move(a1);
}
void main()
{
// How to define these checks?
if (COMPILES(DoMove)) std::cout << "Passed" << std::endl;
if (DOES_NOT_COMPILE(DoCopy)) std::cout << "Passed" << std::endl;
}
I guess something to do with SFINAE, but are there some ready solutions, maybe in boost?
template<class T>struct sink{typedef void type;};
template<class T>using sink_t=typename sink<T>::type;
template<typename T, typename=void>struct my_test:std::false_type{};
template<typename T>struct my_test<T,
sink_t<decltype(
put code here. Note that it must "fail early", ie in the signature of a function, not in the body
)>
>:std::true_type {};
The above generates a test if the "put code here" can be evaluated.
To determine if "put code here" cannot be evaluated, negate the result of the test.
template<class T>using not_t=std::integral_constant<bool, !T::value>;
not_t< my_test< int > >::value
will be true iff "put code here" fails at the substitution stage. (or you can do it more manually, by swapping std::true_type and std::false_type above).
Failing at the substitution stage is different than general failure, and as it has to be an expression you are somewhat limited in what you can do. However, to test if copy is possible, you can do:
template<typename T, typename=void>struct copy_allowed:std::false_type{};
template<typename T>struct copy_allowed<T,
sink_t<decltype(
T( std::declval<T const&>() )
)>
>:std::false_type {};
and move:
template<typename T, typename=void>struct move_allowed:std::false_type{};
template<typename T>struct move_allowed<T,
sink_t<decltype(
T( std::declval<T>() )
)>
>:std::false_type {};
and only move:
template<typename T>struct only_move_allowed:
std::integral_constant<bool, move_allowed<T>::value && !copy_allowed<T>::value >
{};
The general technique above relies on SFINAE. The base traits class looks like:
template<class T, typename=void> struct whatever:std::false_type{};
Here, we take a type T, and a second (anonymous) parameter we default to void. In an industrial strength library, we'd hide this as an implementation detail (the public trait would forward to this kind of private trait.
Then we specialize.
template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{};
the trick is that we make /*some type expression*/ evaluate to the type void if and only if we want our test to pass. If it fails, we can either evaluate to a non-void type, or simply have substitution failure occur.
If and only if it evaluates to void do we get true_type.
The sink_t< some type expression> technique takes any type expression and turns it into void: basically it is a test for substitution failure. sink in graph theory refers to a place where things flow into, and nothing comes out of -- in this case, void is nothing, and the type flows into it.
For the type expression, we use decltype( some non-type expression ), which lets us evaluate it in a "fake" context where we just throw away the result. The non-type expression is now being evaluated only for the purpose of SFINAE.
Note that MSVC 2013 has limited or no support for this particular step. They call it "expression SFINAE". Alternative techniques have to be used.
The non-type expression gets its type evaluated. It isn't actually run, and it does not cause ODR usage of anything. So we can use std::declval<X>() to generate "fake" instances of a type X. We use X& for lvalues, X for rvalues, and X const& for const lvalues.
You're looking for type traits, defined in <type_traits>, to test whether types have certain properties.
If the goal is to ensure that the code won't compile, you can't
have it as part of your test program, since otherwise, your test
program won't compile. You have to invoke the compiler on it,
and see what the return code is.
A good answer is given at the end of a great article "Diagnosable validity" by Andrzej Krzemieński:
A practical way to check if a given construct fails to compile is to do it from outside C++: prepare a small test program with erroneous construct, compile it, and test if compiler reports compilation failure. This is how “negative” unit tests work with Boost.Build. For an example, see this negative test form Boost.Optional library: optional_test_fail_convert_from_null.cpp. In configuration file it is annotated as compile-fail, meaning that test passes only if compilation fails.
for example this std::is_nothrow_move_assignable<std::string>::value returns true in compile-time.
for more checkers see https://en.cppreference.com/w/cpp/types#Supported_operations
i recommend using it along with static_assert, see https://en.cppreference.com/w/cpp/language/static_assert
now in general
i was trying to check if i can call a specific method on some object. this boils down to "assert if this code compiles" and there is a neat and short way to check it.
template<typename T> using canCallPrintYesOn = decltype(::std::declval<T>().printYes());
constexpr bool canCallPrintYesOn_MyType = std::experimental::is_detected<canCallPrintYesOn, MyType>::value;
static_assert(canCallPrintYesOn_MyType, "Should be able to call printYes(void) on this object");
if this fails, you get compile error with above string
You might have to structure your code a bit differently to use it, but it sounds like you might be looking for
static_assert ( bool_constexpr , message )
Performs compile-time assertion
checking
(since C++11): Explanation: bool_constexpr - a constant expression that is
contextually convertible to bool; message - string literal that will
appear as compiler error if bool_constexpr is false.
A static assert declaration may appear at block scope (as a block
declaration) and inside a class body (as a member declaration)
In short:
How to write a test, that checks that my class is not copyable or copy-assignable, but is only moveable and move-assignable?
In general:
How to write a test, that makes sure that a specific code does not compile? Like this:
// Movable, but non-copyable class
struct A
{
A(const A&) = delete;
A(A&&) {}
};
void DoCopy()
{
A a1;
A a2 = a1;
}
void DoMove()
{
A a1;
A a2 = std::move(a1);
}
void main()
{
// How to define these checks?
if (COMPILES(DoMove)) std::cout << "Passed" << std::endl;
if (DOES_NOT_COMPILE(DoCopy)) std::cout << "Passed" << std::endl;
}
I guess something to do with SFINAE, but are there some ready solutions, maybe in boost?
template<class T>struct sink{typedef void type;};
template<class T>using sink_t=typename sink<T>::type;
template<typename T, typename=void>struct my_test:std::false_type{};
template<typename T>struct my_test<T,
sink_t<decltype(
put code here. Note that it must "fail early", ie in the signature of a function, not in the body
)>
>:std::true_type {};
The above generates a test if the "put code here" can be evaluated.
To determine if "put code here" cannot be evaluated, negate the result of the test.
template<class T>using not_t=std::integral_constant<bool, !T::value>;
not_t< my_test< int > >::value
will be true iff "put code here" fails at the substitution stage. (or you can do it more manually, by swapping std::true_type and std::false_type above).
Failing at the substitution stage is different than general failure, and as it has to be an expression you are somewhat limited in what you can do. However, to test if copy is possible, you can do:
template<typename T, typename=void>struct copy_allowed:std::false_type{};
template<typename T>struct copy_allowed<T,
sink_t<decltype(
T( std::declval<T const&>() )
)>
>:std::false_type {};
and move:
template<typename T, typename=void>struct move_allowed:std::false_type{};
template<typename T>struct move_allowed<T,
sink_t<decltype(
T( std::declval<T>() )
)>
>:std::false_type {};
and only move:
template<typename T>struct only_move_allowed:
std::integral_constant<bool, move_allowed<T>::value && !copy_allowed<T>::value >
{};
The general technique above relies on SFINAE. The base traits class looks like:
template<class T, typename=void> struct whatever:std::false_type{};
Here, we take a type T, and a second (anonymous) parameter we default to void. In an industrial strength library, we'd hide this as an implementation detail (the public trait would forward to this kind of private trait.
Then we specialize.
template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{};
the trick is that we make /*some type expression*/ evaluate to the type void if and only if we want our test to pass. If it fails, we can either evaluate to a non-void type, or simply have substitution failure occur.
If and only if it evaluates to void do we get true_type.
The sink_t< some type expression> technique takes any type expression and turns it into void: basically it is a test for substitution failure. sink in graph theory refers to a place where things flow into, and nothing comes out of -- in this case, void is nothing, and the type flows into it.
For the type expression, we use decltype( some non-type expression ), which lets us evaluate it in a "fake" context where we just throw away the result. The non-type expression is now being evaluated only for the purpose of SFINAE.
Note that MSVC 2013 has limited or no support for this particular step. They call it "expression SFINAE". Alternative techniques have to be used.
The non-type expression gets its type evaluated. It isn't actually run, and it does not cause ODR usage of anything. So we can use std::declval<X>() to generate "fake" instances of a type X. We use X& for lvalues, X for rvalues, and X const& for const lvalues.
You're looking for type traits, defined in <type_traits>, to test whether types have certain properties.
If the goal is to ensure that the code won't compile, you can't
have it as part of your test program, since otherwise, your test
program won't compile. You have to invoke the compiler on it,
and see what the return code is.
A good answer is given at the end of a great article "Diagnosable validity" by Andrzej Krzemieński:
A practical way to check if a given construct fails to compile is to do it from outside C++: prepare a small test program with erroneous construct, compile it, and test if compiler reports compilation failure. This is how “negative” unit tests work with Boost.Build. For an example, see this negative test form Boost.Optional library: optional_test_fail_convert_from_null.cpp. In configuration file it is annotated as compile-fail, meaning that test passes only if compilation fails.
for example this std::is_nothrow_move_assignable<std::string>::value returns true in compile-time.
for more checkers see https://en.cppreference.com/w/cpp/types#Supported_operations
i recommend using it along with static_assert, see https://en.cppreference.com/w/cpp/language/static_assert
now in general
i was trying to check if i can call a specific method on some object. this boils down to "assert if this code compiles" and there is a neat and short way to check it.
template<typename T> using canCallPrintYesOn = decltype(::std::declval<T>().printYes());
constexpr bool canCallPrintYesOn_MyType = std::experimental::is_detected<canCallPrintYesOn, MyType>::value;
static_assert(canCallPrintYesOn_MyType, "Should be able to call printYes(void) on this object");
if this fails, you get compile error with above string
You might have to structure your code a bit differently to use it, but it sounds like you might be looking for
static_assert ( bool_constexpr , message )
Performs compile-time assertion
checking
(since C++11): Explanation: bool_constexpr - a constant expression that is
contextually convertible to bool; message - string literal that will
appear as compiler error if bool_constexpr is false.
A static assert declaration may appear at block scope (as a block
declaration) and inside a class body (as a member declaration)