4

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.

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • 6
    Use `= delete`. That's what it's there for. – Nicol Bolas Jan 12 '16 at 13:59
  • [`static_assert`](http://en.cppreference.com/w/cpp/language/static_assert) can't really stop a function from being called, it's purpose is for compile-time assertions which gives errors. You might want to look at e.g. [`std::enable_if`](http://en.cppreference.com/w/cpp/types/enable_if) instead. – Some programmer dude Jan 12 '16 at 13:59
  • 3
    Why does the function exist, when it is not meant to be called? Simply remove it from your codebase. – cdonat Jan 12 '16 at 14:08
  • 1
    The more I re-read your question, the more confused I get, what do you mean by "prevent certain functions from being called"? Do you want the function to not exist at all (which is what using `= delete` does, as does actually removing it)? Do you want to get a compile-time error depending on some specific condition? Do you want to get a compiler error if called using some specific arguments, or if called from only some places? Do you want to stop someone from calling the function depending on some run-time condition? – Some programmer dude Jan 12 '16 at 14:11
  • @JoachimPileborg `std::enable_if` does not apply here. The function is forbidden unconditionally. If I simply remove the function, overload resolution will chose another function, which I don't want. – Lingxi Jan 12 '16 at 14:14
  • @cdonat The function is there so that overload resolution does not choose another one. I want to prevent certain use, which would otherwise compile fine with overload resolution. – Lingxi Jan 12 '16 at 14:16
  • Then `= delete` is the way I recommend. The function will still be available for overload resolution, but if selected the compiler will say that it doesn't exist. – Some programmer dude Jan 12 '16 at 14:16
  • @JoachimPileborg Yeah, I guess `= delete` may be the only way. I just want to provide some custom diagnostic message to let the user know what is really going wrong and how to fix it. – Lingxi Jan 12 '16 at 14:18
  • I found a similar question here: [link](https://stackoverflow.com/questions/4694896/generating-link-time-error-for-deprecated-functions?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa). If you cannot remove the function you may remove implementation which is similar to `= delete`, but only causes link-time error. Your code will compile if nobody calls that function. – Jónás Balázs Jun 04 '18 at 15:23

3 Answers3

9

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.)

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • Would `always_false::value` and simply `false` make a difference? – Lingxi Jan 12 '16 at 14:25
  • It does make a difference! I guess this would be the solution I want. Thanks :) – Lingxi Jan 12 '16 at 14:33
  • Yes, the trick is to delay the determination of the condition until the type arguments are known. Since `false` does not depend on the parameters, it is evaluated right away. `always_false::value`, while – well – always false, technically still depends on `Ts...`. – 5gon12eder Jan 12 '16 at 17:46
4

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");
Community
  • 1
  • 1
0

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!");
}
davidhigh
  • 14,652
  • 2
  • 44
  • 75