2016-05-31 217 views
21

在下面的玩具示例中,我想获取函数的名称。该函数本身作为std::function参数给出。在C++中可以获得std::function对象的名称吗?获取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 

否则,我将不得不将该函数的名称作为第二个参数。

+0

是这个例子完整?难道你只是使用预处理器TOSTRING将魔法转换为字符串? printName(TOSTRING(magic)) – thorsan

+11

这是不可能的。一旦编译完成,你会失去所有这些信息,C++没有任何[类型自省](https://en.wikipedia.org/wiki/Type_introspection)。唯一的可能性是依赖于编译器,使用调试信息来尝试找出它。 –

+7

'std :: function'不需要封装任何名字...... –

回答

28

不,没有。函数名称(如变量名称)被编译出来,以便在运行时不可见。

最好的办法是按照您自己的建议传递函数的名称(使用std::stringconst char*)。 (或者,您也可以基地这是在C++ 11引入了__func__的解决方案。)

-4

我认为最简单的解决方法是使用例如像这样:

#include <typeinfo> 

#include <stdio.h> 

void foobar(void) 
{ 
} 

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

现在,它有很多缺点,我不会推荐使用它。 首先,IIRC会显示函数的符号,而不是您在源代码中使用的名称。此外,名称将从编译器更改为编译器。 最后,RTTI很慢。

此外,我不知道它如何与std :: function一起使用。老实说,从来没有用过。

+5

https://ideone.com/fjfNpL这里你去 – coyotte508

+2

这个名字将是'std :: function <...>'类型,而不是'std :: function' *保存的*。 –

+2

嘿,这个工程:https://ideone.com/0Iz76j – Henrik

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

这通常是一个坏主意。

6

给定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?我不知道谁在处理细节),在包含<cxxabi.h>后需要使用abi::__cxa_demangle以获得人类可读的类型名称版本。

编辑
作为马修M.指出,给定一个函数指针,这个返回的类型将只是函数的签名。例如:

int function(){return 0;} 
printName(function); 

这将输出(假设你如果有必要demangled),这是不是函数的名称int (*)()

这种方法将带班的工作,虽然:

struct Function 
{ 
    int operator()(){return 0;} 
}; 

printName(Function{}); 

这将打印Function根据需要,但当时并没有函数指针工作。

+0

和Visual Studio实际上是MSVC:p – coyotte508

+1

函数的名称和它的签名有很大的区别;在[gcc]上(http://ideone.com/Gp3oWQ)我得到'PFvvE'作为'void magic()'的显示,因为只有签名被编码。不确定关于MSVC。 –

2

你也可以拥有与名称的字符串参数的函数,然后使用宏来调用它

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

example