2017-02-09 651 views
2

我想在std::map中存儲和識別std::function對象。 確定我想使用std::function::target。 如果我使用std::bind綁定到類的成員函數,我無法從std::function::target得到指針。C++ 11獲取std :: function的指針

#include <functional> 
#include <iostream> 


using namespace std; 
void normal_fun(int a) 
{ 
    std::cout << "hello\n"<<a; 
} 

class testclass 
{ 
public: 
    int b; 
    void mytest(int a) 
    { 
     b = a; 
    } 
}; 

uintptr_t getFuncAddress(std::function<void(int)> &f) 
{ 
    uintptr_t returnAddress = reinterpret_cast<uintptr_t>(f.target<void(*)(int)>()); 
    return returnAddress; 
} 

int main() 
{ 

    testclass c; 
    std::function<void(int)> f1 = bind(&testclass::mytest,&c, placeholders::_1); 
    std::function<void(int)> f2 = normal_fun; 
    auto addrF1 = getFuncAddress(f1); 
    auto addrF2 = getFuncAddress(f2); 
} 

我該如何實現自己想做的事?

+1

你不會在'std :: function'中存儲實際的'void(int)',所以'target'返回一個'nullptr'。您存儲綁定的結果,這是一些未知的函子類型。 – StoryTeller

+0

這就是我所害怕的...你知道我怎麼寫'target'來獲得綁定的指針? – bdahl

+1

如果您需要這樣做,我認爲您正在接觸錯誤的工具。 'std :: function'是關於類型擦除。試圖通過類型來獲取確切的目標有點像用'dynamic_cast's亂丟你的代碼,而不是依賴於多態性。 – StoryTeller

回答

0

我想存儲和識別std :: map中的std :: function對象。

我假設,你想識別std::function對象(例如,要選擇調用或刪除它們)。 在這種情況下,我使用了一個附加的鍵,例如一個簡單的unsigned。 全局函數可以使用:

typedef unsigned Key; 

Key genId() 
{ 
    static Key id = 0; 
    return ++id; 
} 

現在,std::function對象可以用此鍵配對。該API可以授予對配對std::function對象的訪問可以專門使用該密鑰完成。

+0

問題在於,包含相同目標的兩個'std :: function'對象將具有不同的鍵,它們可能不符合OP的需求。 –

+0

@JonathanWakely是的,我期待這個答案。我們使用受sigC++啓發的信號插槽概念。我們試圖通過「平衡」訪問和跟蹤連接來防止這種情況。最後,兩個相同的目標是允許的,並且可能(在極少數情況下)是一個功能而不是bug。 – Scheff

+0

關於此主題我認爲這是將隨機密鑰放入包含我的成員函數的類中的最佳方式。 – bdahl

1

A std::function不是函數指針。它甚至不是指針。

如果您知道std::function中存儲的是什麼類型,您可以獲取指向其存儲內容的指針。請注意,這裏的指針不是函數指針,而是指向函數指針的指針。

如果你不知道它存儲的是什麼類型,你不能。

如果你想<==hash(etc),std::function不提供此爲你。它鍵入刪除(),複製,移動,銷燬,typeid和投回到原始。

您可以使用類型擦除技術來增加std::function這些操作;注意二進制操作的類型擦除比一般的擦除類型更容易觸摸。

刪除某些東西的類型不應該是您在解決問題時的第一步,但它可以解決您的問題。有關C++的SO文檔中的類型擦除的文章。這不是初學者主題。

賠率是你可以用更簡單的方法解決你的潛在問題。

在任何情況下,使用從target返回的類型進行排序並不是一個好主意,因爲它是指向可能自動存儲對象或可能堆存儲對象的指針;其中兩項將在無害操作(如移動)下具有明顯不同的失效規則。一個SBO(小緩衝區優化)目標將隨着std::function的實例而移動,而堆分配的一個可能會停留在std::function類似於std::move類似操作的狀態。這真是一團糟。

0
f.target<void(*)(int)> 

是錯誤的,因爲通過綁定返回的類型不是void(*)(int),使目標絕不會返回一個非空指針。綁定不返回一個函數指針。它返回一些實現指定類型的對象。

該類型可以通過decltype獲得。這是一個有點乖張,但應該是正確的:

f.target<decltype(bind(
    &testclass::mytest, 
    (testclass*)nullptr, 
    placeholders::_1) 
)> 

,但我有沒有在getFuncAddress知道如果我有識別TestClass或myOtherClass或別人類的問題

你如果知道包裝對象的類型,則只能使用std::function::target。如果選項數量有限,那麼您可以嘗試使用每種類型獲取目標,直到返回非null值。對於任意未知類型,std::function::target不能用於您。


我想存儲在一個std確定的std ::函數對象::地圖

我認爲你將不得不使用一些外部密鑰,不能從std::function提取。這意味着如果你想防止重複,你還需要一個外部方法來保證從鍵到函數的唯一映射。

+0

好的,這是好的,但我有問題,我不知道'getFuncAddress'如果我有'testclass'或'myOtherClass'或其他類。我知道只有成員函數的簽名是'void(int)'... – bdahl

+0

@bdahl請參閱編輯 – user2079303

1

std::function的要點是爲符合給定簽名的可調用對象提供統一的接口和類型。你期待它也提供了一個統一的排序鍵,但它不這樣做。沒有辦法對std::bind返回的函數指針和任意函數對象和可調用對象進行排序。它們是完全不同的東西,比較它們是沒有意義的。所有std::function允許你做的是存儲他們並給他們打電話。

如果你需要一個排序機制,你將不得不自己發明一些東西,你不會從std::function得到它。

+2

「*如果你需要一個排序機制,你將不得不自己創造一些東西,你不會從'std :: function'中得到它。*」這就像把馬車放在馬前面一樣。如果你需要「發明」一些機制來排序你的對象,你可能不需要首先對它們進行排序。另一方面,如果你需要對它們進行排序,那麼你就應該知道你正試圖解決的問題的排序條件,並且你可以使用這些信息來產生散列或排序機制。 – Pixelchemist

+0

@Pixelchemist是偉大的一點。我認爲關鍵在於你是否「需要」它,如你所說,如果你不能描述一個有意義的關鍵詞來排序,你可能不需要它。 –