【问题标题】:Get return type of current function in C++在 C++ 中获取当前函数的返回类型
【发布时间】:2022-01-03 22:18:26
【问题描述】:

此问题与Get return type of function in macro (C++) 类似,但已有 10 年历史,没有人回答。任何其他解决方案都将被接受。

我想创建一个仅在不满足条件时从函数返回的断言宏,例如:

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

如果包含函数返回 void,这将不起作用,但我不想创建 2 个宏。我希望能够在不更改代码的情况下添加/删除返回值。我的想法是创建一个辅助函数:

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

现在宏可以像这样工作:

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;
}

但我需要当前函数的返回类型 T 而不必调用 ASSERT(i != nullptr, double) 之类的东西,因为这样我就可以简单地使用 2 个宏。此外,像 __func__ 和 std::source_location 这样的宏只是字符串。

【问题讨论】:

  • return {} 不起作用,但 return void() 起作用。
  • 当我做这样的事情时,我最终编写了 3 个单独的宏,它们都称为一个通用宏。我曾经必须返回的唯一值是-1nullptrfalse。编辑:实际上,我只是偷看了代码,并没有 void 案例 - 这样的函数应该如何指示错误?
  • @o11c 例如,我有自己的分配器,我不在乎是否有人调用 free(nullptr),但是是的,这个问题更多地是关于 c++ 的限制,而不是我遇到的真正问题。
  • ASSERT 宏中隐藏像return 这样的操作可能会令人惊讶,并且会使代码不那么明显。如果有很多地方需要这个宏,我会考虑将其拆分成更小的部分Bob大叔推荐的函数
  • 是否可以选择使用 throw 而不是 return?那么你也不会对返回值有任何问题。

标签: c++ c++20 template-meta-programming


【解决方案1】:

这可以使用__PRETTY_FUNCTION__ (GCC, Clang) /__FUNCSIG__ (MSVC) 来实现,这是一种非标准扩展,可为您提供当前函数的名称,包括返回类型。

您可以在编译时分析字符串以查看其中是否包含void

#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);
}

这需要更多的测试,以确保您不能用尾随返回类型破坏它,并在函数定义中添加各种东西(属性、调用约定等)。

【讨论】:

  • 不错 :-) 这是一个使用 std::source_locationminor tweakfunction_name() 返回的字符串也有实现定义的格式,但是可以跳过宏测试。
【解决方案2】:

如果您不知道函数的返回类型,则没有表达式X,其中语句return X;void-returning 函数和返回任意类型的函数都有效。

可以创建一个特殊的用户定义模板类型,其中return X; 将适用于多种类型。例如,return std::nullopt; 对于返回任何optional&lt;T&gt; 的函数始终有效。您可以从像 nullopt 这样的对象创建具有类似隐式转换属性的类似类型,该类型具有允许“携带”void 类型的特化(不允许使用std::optional&lt;void&gt;,尽管您可以使用无状态类型相当于void)。

当然,这现在需要调用者从您的类型中提取返回值(如果有的话)。这也意味着它必须检查是否返回了一个值。

expected&lt;T, E&gt; 类型(其中有几种实现)代表了您所说的类型。不同之处在于它们携带TET == void 的特殊情况),其中携带E 表示预期返回的消费者的错误值处理。

但所有这些都需要更改函数的实际返回值。如果你一心只想使用简单的类型,如voiddoubleint 等,那么无论你在做什么,你都必须有不同的宏。

【讨论】:

  • 一种无状态类型,等同于void,又名单元类型,例如std::monostate.
  • return f&lt;T&gt;(); 在 f 和当前函数返回相同类型时有效,即使是 void。 AFAIU OP 的问题更多是缺少decltype(return) 或其他自省功能。
  • @MarcGlisse:是的,如果您可以获取当前函数的返回类型,那么您可以这样做。但既然你不能,我的观点仍然成立。
猜你喜欢
  • 2014-02-20
  • 2012-01-29
  • 1970-01-01
  • 2016-07-01
  • 2018-11-06
  • 2015-09-14
  • 2011-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多