Ferenc Deak
Ferenc Deak

Reputation: 35418

Is it possible to check if the function has a void return type

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

Answers (5)

Anton K
Anton K

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

PaperBirdMaster
PaperBirdMaster

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.

Check it online

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

Quentin
Quentin

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

Teivaz
Teivaz

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

Mike Vine
Mike Vine

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

Related Questions