2016-05-31 46 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