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