Reputation: 35418
This very strange request came up ...
I need to do a compile time check whether the current function has a void
return type or not and fail compilation if the return type is void
.
I tried to do some magic with http://en.cppreference.com/w/cpp/types/result_of and decltype
also, however I just cannot get closer to a solution.
#include <type_traits>
void other_func(void) { }
void test_maxi(void)
{
typedef decltype(&::other_func) TYPE;
static_assert(std::is_same<TYPE, void>::value, "not void");
}
int main() {
}
So here comes the question:
Is it possible to do this for the current function?
EDIT The return type check should go in a macro, since it will be used in several functions.
Upvotes: 10
Views: 9063
Reputation: 670
You can implement compile-time checking if one string literal starts with another, and use the __PRETTY_FUNCTION__
macro, which is set to string literal that starts with function return type. You should check if this macro starts with void
followed by space.
This code compiles fine:
constexpr bool startsWith(const char* a, const char* b) {
return *a == *b && (*(a + 1) == '\0' || startsWith(a + 1, b + 1));
}
int f() {
static_assert(!startsWith("void ", __PRETTY_FUNCTION__), "not void");
return 1;
}
int main() {
}
If you change f
return type to void
:
constexpr bool startsWith(const char* a, const char* b) {
return *a == *b && (*(a + 1) == '\0' || startsWith(a + 1, b + 1));
}
void f() {
static_assert(!startsWith("void ", __PRETTY_FUNCTION__), "not void");
}
int main() {
}
static_assert
will fire.
The __PRETTY_FUNCTION__
macro seems to be specific to GNU C++ Compiler, however, clang++
works fine since it defines this macro as well. If you are using another compiler, you should check if this macro is really being set, and if not, read compiler documentation in order to determine similar
macro, e.g., __FUNCSIG__
.
You can play around with #ifdef __PRETTY_FUNCTION__ ...
to make this more portable between various compilers, but I believe this is a topic for another question.
Upvotes: 14
Reputation: 13320
Try this:
template <typename ... Args>
constexpr bool return_void(void(Args ...)) { return true; }
template <typename R, typename ... Args>
constexpr bool return_void(R(Args ...)) { return false; }
Let's suppose we have the following functions:
void f() {}
void g(int) {}
void h(int*,char&) {}
void i(float,bool) {}
void j(const char *const) {}
void k(void()) {}
int l() { return {}; }
float m(int) { return {}; }
All the calls to return_void
will return true as long as it is called with the first six functions, the return_void(l)
and return_void(m)
call will return false because they will invoke the second version of the template, the one returning false.
This will allow you to check if a function return void
both at runtime and compile time:
int main() {
static_assert(return_void(f), "not void");
if (!return_void(m))
std::cout << "m result is: " << m(0) << '\n';
return 0;
}
Upvotes: 6
Reputation: 63134
You can check the return type of the current function by checking whether a dead return
can compile:
struct ConvertToAnything {
template <class T>
operator T() const { assert(!"Don't call me!"); }
};
#define CHECK_NONVOID_RETURN() if(true); else return ConvertToAnything{}
int f() {
CHECK_NONVOID_RETURN(); // Passes
return 42;
}
void g() {
CHECK_NONVOID_RETURN(); // Fails at compile-time
}
The case of void
is a special one, so this is enough. But you can also forbid other types by overloading ConvertToAnything::operator Type() = delete;
. Allowing void
is a bit more involved, but still feasible.
Upvotes: 3
Reputation: 5675
All you need is to detect the return type and check if it is of type void. To check the type you can use std::is_void
. return type can be detected with following template:
#include <type_traits> // for is_void
template<typename R, typename... A>
void test(R (*func)(A...)) {
static_assert(
!std::is_void<R>::value,
"void return type is not allowed"
);
}
// functions to check:
void a(int, char*) {}
int b(char, float) {return 0;}
int main()
{
test(a); // < assert triggered here
test(b);
}
Upvotes: 0
Reputation: 9837
If you can name the current function then the simplest way is:
static_assert(!std::is_same<decltype(test_maxi()), void>::value, "void");
Upvotes: 6