Home > Blockchain >  Get return type of current function in C
Get return type of current function in C

Time:01-03

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.

CodePudding user response:

There is no expression X where the statement return X; will be valid for both a void returning function and a function that returns an arbitrary type.

One can create a special user-defined template type where return X; will be valid for a variety of types. For example return std::nullopt; is always valid for a function returning any optional<T>. You can create a similar type with similar implicit conversion properties from an object like nullopt, one with a specialization to allow a void type to be "carried" (std::optional<void> is not allowed, though you could use a stateless type as an equivalent to void).

Of course, this now requires the caller to extract the returned value (if any) from your type. This also means that it has to check to see if a value was returned or not.

The expected<T, E> type (of which there are several implementations) represents a type like what you're talking about. The difference is that they carry either a T or E (with a special case for T == void), where carrying an E represents an error value that the consumer of the return is expected to handle.

But all these require changing the function's actual return value. If you're dead-set on using simple types like void, double, int, etc, then you're going to have to have different macros for whatever return you're doing.

CodePudding user response:

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

  • Related