获取std::function的名称

24
在下面这个玩具示例中,我想获取函数的名称。该函数本身作为std::function参数给出。在C++中是否有可能获得std::function对象的名称?
void printName(std::function<void()> func){
    //Need a function name()
    std::cout << func.name();
}

void magic(){};

//somewhere in the code
printName(magic());

output: magic
否则,我将不得不将函数名称作为第二个参数给出。

11
不可能的。一旦编译完成,就会失去所有类似的信息,C++没有任何类型内省的功能。唯一的可能性是依赖于编译器,使用调试信息来尝试找到它。 - Some programmer dude
7
std::function不需要封装任何具有名称的内容... - Kuba hasn't forgotten Monica
你为什么要这样做呢? - KaareZ
printName(magic()); 无法编译。 - user253751
Lambda表达式的名称是什么? - Caleth
显示剩余2条评论
6个回答

29

没有,函数名(像变量名一样)会在编译时被删除,因此在运行时不可见。

你最好的选择是像你自己建议的那样传递函数名(使用std::stringconst char*)。 (或者你可以基于C++11引入的__func__解决方案。)


13
答案是否定的,但您可以制作类似的东西。
template<class R, class... Args>
class NamedFunction
{
public:
    std::string name;
    std::function<R(Args...)> func;
    NamedFunction(std::string pname, std::function<R(Args...)> pfunc) : name(pname), func(pfunc)
    {}
    R operator()(Args&&... a)
    {
       return func(std::forward<Args>(a)...);
    }
};

然后定义一个预处理器

#define NAMED_FUNCTION(var, type, x) NamedFunction<type> var(#x,x)
...
NAMED_FUNCTION(f, void(), magic);

7

给定一个 std::function,它有一个成员函数叫做 target_type,可以返回存储的函数对象的 typeid。这意味着您可以执行以下操作:

void printName(std::function<void()> func){
    //Need a function name()
    std::cout << func.target_type().name();
}

这将返回一个每种类型都唯一的实现定义字符串。在Visual Studio中,此字符串已经可读。在gcc(或者可能是glibc?我不知道谁负责详细内容)中,您需要在包含后使用abi::__cxa_demangle来获取类型名称的可读版本。
编辑 正如Matthieu M.指出的那样,给定函数指针,此功能返回的类型将只是函数签名。例如:
int function(){return 0;}
printName(function);

这将输出(假设您已解码必要的内容)int (*)(),这不是函数的名称。
但是,此方法可用于类:
struct Function
{
    int operator()(){return 0;}
};

printName(Function{});

这将按预期打印Function,但对于函数指针无效。

Visual Studio实际上就是MSVC :p - coyotte508
1
函数名和函数签名之间有很大的区别;在gcc上,我得到了PFvvE作为void magic()的显示,因为只有函数签名被编码。不确定MSVC是否也是如此。 - Matthieu M.

3
你可以在函数中添加一个字符串参数来表示名称,然后使用宏来调用它。
void _printName(std::function<void()> func, const std::string& funcName){
    std::cout << funcName;
}
#define printName(f) _printName(f, #f)

void magic(){};

//somewhere in the code
printName(magic);

请看示例

1
保持自己的映射从函数指针到名称。
template<class Sig>
std::map<Sig*, const char*>& name_map() {
  static std::map<Sig*, const char*> r;
  return r;
}

struct register_name_t {
  template<class Sig>
  register_name_t( Sig* sig, const char* name ) {
    name_map()[sig]=name;
  }
};
#define TO_STRING(A) #A
#define REGISTER_NAME(FUNC) \
   register_name_t FUNC ## _register_helper_() { \
     static register_name_t _{ FUNC, TO_STRING(FUNC) }; \
     return _; \
   } \
   static auto FUNC ## _registered_ = FUNC ## _register_helper_()

只需执行REGISTER_NAME(magic);即可将名称magic注册到函数magic。这应该在文件范围内完成,可以在头文件或cpp文件中完成。

现在我们检查std::function是否有与其签名匹配的函数指针存储在其中。 如果是这样,我们会在name_map中查找它,并在找到时返回名称:

template<class Sig>
std::string get_function_name( std::function<Sig> const& f ) {
  auto* ptr = f.target<Sig*>();
  if (!ptr) return {};
  auto it = name_map().find(ptr);
  if (it == name_map().end()) return {};
  return it->second;
}

这通常不是一个好主意。

-4
我认为最简单的解决方案是使用typeid(fun).name(),例如像这样:
#include <typeinfo>

#include <stdio.h>

void foobar( void )
{
}

int main()
{
  printf( "%s\n", typeid( foobar ).name() );
  return 0;
}

现在,它有很多缺点,我不建议使用它。 首先,如果我没记错的话,它显示的是函数的符号,而不是你在源代码中使用的名称。 此外,名称会因编译器而异。 最后,RTTI 很慢。

[编辑] 另外,我不确定它如何与 std::function 一起使用。老实说,我从未使用过。


3
名称将是 std::function<...> 类型,而不是 std::function 所包含的内容。 - Nicol Bolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接