Forbids functions with `static_assert` - c++

I want to prevent certain functions from being called. Let's ignore the case of calling the function via a function pointer or something, and just concentrate on the case of direct function call. I can do this with = delete. However, the diagnostic issued is not quite informative. I considered using static_assert, with which you can supply a custom diagnostic message. I placed a static_assert(false, ...) statement within the function body, hoping that it fires when the function is called. However, it turns out that the static_assert fails even if the function is not called. Any suggestions?
Additional Note: The function is forbidden unconditionally. So, std::enable_if does not apply here. The motivation for such a function is that I want to prevent certain use, which would otherwise compile fine with overload resolution. So I can't just remove the function. deprecated is not what I want. I want a compilation error, not a warning.

I agree with others that you shouldn't use static_assert for this at all and mark the function as deprecated instead.
static_assertions fire at the time they are compiled. For an ordinary function, this is the time it is parsed, not the time it is called. For a template, however, it is the time of instantiation. So you can make your function a template like this.
template <typename...>
struct always_false { static constexpr bool value = false; };
template <typename... Ts>
void
never_call_me(Ts&&...)
{
static_assert(always_false<Ts...>::value,
"You should have never called this function!");
}
If typename... is not right for you (because the function is overloaded), try narrowing it down to only match what you want to make an error.
The trick used here is that always_false<Ts...>::value depends on the type parameters Ts... so it cannot be evaluated until the template is instantiated. (Even though we can clearly see that it will always be false.)

If it is a member function then = delete is your best (most portable) bet. Otherwise, both GCC and MSVC have support for marking a function as "deprecated", which will cause the compiler to issue a warning when the function is called.
From C++ mark as deprecated:
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif
Usage:
DEPRECATED(void badidea(int a, const char* b));
.... and now with C++ 14, we can write it as:
#define DEPRECATED(func, reason) [[deprecated(reason)]] func
With usage:
DEPRECATED( void badidea(int a, const char* b), "This function was a bad idea");

As a shorter version of #5gon12eder's good answer, you can simply use
template<typename ... Ts>
void never_call_me(Ts&&...ts)
{
static_assert(not (std::is_same_v<Ts,Ts> && ...),
"You should have never called this function!");
}

Related

Is unpacking variadic using array(or initializer_list) trick optimize-safe?

Since C++14 cannot use fold expression, in order to make a function that calls bar on each variadics, one have to use function overloading.
template<typename Arg>
void foo(Arg arg) {
bar(arg);
}
template<typename Arg, typename ...Args>
void foo(Arg arg, Args... args) {
bar(arg);
foo(args...);
}
But using comma operator and parenthesis in bracket, one can unpack without overload.
template<typename ...Args>
void foo(Args... args) {
int dummy[] = {
(bar(args), 0)...
};
}
It works well as expected, but my compiler always warns me that dummy is not used.
So I'm worried that the compiler removes dummy(since it's unused and actually initialized only by literals), and resulting to not calling bar.
Or just declaring dummy as volatile is enough to be guarantee that bar is called?
I know that there is [[maybe_unused]] attribute, but it's also C++17 feature.
So I'm worried that the compiler removes dummy(since it's unused and actually initialized only by literals), and resulting to not calling bar.
Compiler might remove dummy but cannot remove bar calls if they have side effects (as for the recursive version).
So you are safe.
Or just declaring dummy as volatile"
That is worst. As you force the write. You remove the warning the wrong way.
I know that there is [[maybe_unused]] attribute, but it's also C++17 feature.
Casting to void is a common way to remove the warning, previously.
no worry, the compiler optimizations won't break the code behavior otherwise the compiler is broken, however there is copy eliding and such which changes the behavior in a documented way .
in your case the the call will be evaluated and the return value stored in the array is discarded. since this is your intention you can get rid of this warning by using the variable !
((void)dummy);

Implement assert without preprocessor in C++20

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; });

While trying to use SFINAE to disable functions, have I created undefined behavior?

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.

deleted functions with a compile time message when they are used

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)

GCC 4.9 gives error when checking inline function's pointer (with static_assert)

Consider the following case
typedef void (*foo)();
template<foo f>
struct bar {
static_assert(f!=nullptr,"f == null!");
};
void baz() {}
inline void bax() { }
bar<baz> ok;
bar<bax> bad; // error: non-constant condition for static assertion
Both baz and bax are accepted as template arguments.
It indicates that both are accepted as constants.
However, at static_assert they appears to be different (at least in gcc 4.9) - bax is not a constant anymore.
My assumption was that static_assert and template evaluate constantness identically.
E.g. either error should be
'bax is not a valid template argument' or
static_assert should not raise non-constant condition error.
Am I wrong?
When a function is inlined, the pointer to the function does not exist. So we can not compare it with nullptr.
Whether a function is eventually inlined or not, depends on the compiler. inline keyword does not guarantee that.
Just yet another GCC bug, update to newer version, or migrate to LLVM (clang).
See issue ticket for details:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52036