Search code examples
c++c++20template-meta-programming

Get return type of current function in C++


This question is similar to Get return type of function in macro (C++) but it is 10 Years old and is not answered. Any other solution would be accepted.

I want to create an assert macro that only returns from the function if the condition isn't met, Like:

#define ASSERT(X) if(!(X)) return {};

This doesn't work if the containing function returns void, but I don't want to create 2 macros. I want to be able to add/remove return values without changing the code. My idea is to create a helper function:

template<class T>
T re(){
    if constexpr( std::is_same<T, void>::value ){
        return;
    }
    else if constexpr( ! std::is_same<T, void>::value ){
        return {};
    }
}

Now the macro could work like:

double f(int *i){
    if(i == nullptr){
        typedef std::invoke_result<decltype(&f),int>::type T; // only works for this function
        return re<T>();
    }
    return 1.0;
}

But I require the return type T of the current function without having to call something like ASSERT(i != nullptr, double) because then I could simply use 2 macros. Also, macros like __func__ and std::source_location are only strings.


Solution

  • This is possible using __PRETTY_FUNCTION__ (GCC, Clang) /__FUNCSIG__ (MSVC), a non-standard extension that gives you the name of the current function, including the return type.

    You can analyze the string at compile-time to see if it has void in it or not:

    #include <string_view>
    
    struct AnyType
    {
        template <typename T>
        operator T()
        {
            return {};
        }
    };
    
    template <bool IsVoid>
    auto foo()
    {
        if constexpr (IsVoid)
            return;
        else
            return AnyType{};
    }
    
    #ifndef _MSC_VER
    #define FUNCNAME __PRETTY_FUNCTION__
    #else
    #define FUNCNAME __FUNCSIG__
    #endif
    
    #define ASSERT(x) if (!bool(x)) return foo<std::string_view(FUNCNAME).starts_with("void ")>()
    
    void x()
    {
        ASSERT(0);
    }
    
    int y()
    {
        ASSERT(0);
    }
    

    This needs more testing to make sure you can't break it with trailing return types, and by adding various stuff to function definition (attributes, calling conventions, etc).