2012-08-16 61 views
2

將原始問題放在底部。C++將函數指針轉換爲唯一的「散列」鍵

我想我明白你們現在在說什麼 - 因爲成員函數指針的內部結構是編譯器/機器特定的,所以我真的不可能這樣做。所以即使它在我測試它的時候工作 - 我不能保證它會在其他編譯器/機器上。

有沒有另外一種方法可以解決我想要的問題?

我有一個模板類和該類的基本模板類,並且我有一個委託類,它包含調用時委託類應調用的所有事件的std :: map。

我需要一張地圖的原因是,既要確保同一個成員函數(指向成員函數的事件)不會更多地增加一次,並使得使用對象和成員從地圖中移除事件成爲可能函數最初在實例化事件對象時使用。

template <class T_arg1> struct EventBase1 
{ 
public: 
    bool operator < (const EventBase1 &event1) const { return _key < event1._key; }; 
    virtual void operator()(T_arg1 t1) const {}; 
    std::pair<intptr_t, intptr_t> _key; 
}; 

template <class T, class T_arg1> struct Event1: public EventBase1<T_arg1> 
{ 
    template <class T_arg1> friend class Delegate1; 
    typedef typename void (T::*Method)(T_arg1); 
private: 
    Method _method; 
    T* _object; 
public: 
    Event1(T* object, Method method): _object(object), _method(method) 
    { 
     _key = std::pair<intptr_t, intptr_t>(reinterpret_cast<intptr_t>(object), reinterpret_cast<intptr_t>(reinterpret_cast<void*&>(method))); 
    }; 
    virtual void operator()(T_arg1 t1) const { 
     (_object->*_method)(t1); 
    }; 
}; 

template <class T_arg1> class Delegate1 
{ 
public: 
    typedef typename EventBase1<T_arg1> T_event; 
    void operator += (T_event* observer) 
    { 
     assert(observer); 
     _observers[*observer] = observer; 
    }; 
    void operator -= (const T_event &observer) 
    { 
     std::map<T_event, T_event*>::iterator i = _observers.find(observer); 
     if(i != _observers.end()) { 
      delete i->second; 
      _observers.erase(i); 
     } 
    }; 
    void operator()(T_arg1 t1) 
    { 
     for(std::map<T_event, T_event*>::iterator i = _observers.begin(); i != _observers.end(); i++) { 
      (*(i->second))(t1); 
     } 
    }; 
private: 
    std::map<T_event, T_event*> _observers;  
}; 

原題:

我存儲函數指針在std::map,和我生成我的鑰匙了圖如下:std::pair<int, int>((int)((int*)object), (int)(static_cast<const void*>(&method)))

method是一個函數(方法)指針,而object是一個指向該方法對象的指針。

它的作品,但我有一個偷偷摸摸的懷疑,我得到的第二部分的關鍵是不完全正確的。

我從來沒有完全理解函數指針,但我想我得到的是指針的地址,而不是函數的地址,編譯器不會讓我這樣做,如((int)(static_cast<const void*>(method)))

所以我的問題是 - 如何從函數指針獲取唯一鍵,如果我稍後從另一個指向相同方法的函數指針獲取鍵,該怎麼辦?

由於提前, 馬丁

+0

「方法是一個函數(方法)指針」 - 最好顯示代碼而不是描述它。我有一個偷偷摸摸的懷疑,它實際上是一個指向成員函數'R(T :: *方法)(A1,A2,A3)'的指針。 – MSalters 2012-08-16 08:29:03

+0

指向 - 是的,它是一個指向成員函數的指針 - 我將編輯我的問題。 – Martin 2012-08-16 09:21:31

+0

改爲存儲'std :: function <>'可能更簡單。 – MSalters 2012-08-16 09:23:52

回答

3

第二同樣是不合法的:正式,你不能一個指針轉換爲 函數的指針數據(和void*是一個指向數據的指針)。 此外,您不能保證能夠將任何指針轉換爲 int;如果int至少與 指針一樣大(這意味着您的代碼在大多數64位系統上無法編譯),則轉換僅爲合法。

有幾種解決方法。首先,在大多數(所有?)現代機器上,功能和功能指針具有相同的 大小和表示。 (Posix實際上需要它,即使它在我使用的第一臺Unix機器上也不是這種情況)。如果我們假設這個, 可以通過使用intptr_t和 來保證足夠大的整數類型編譯器使用額外的間接級別:(這個假設是objectmethod是你的指針指向 對象和功能)

std::pair<intptr_t, intptr_t>(
    reinterpret_cast<intptr_t>(reinterpret_cast<void*&>(object)), 
    reinterpret_cast<intptr_t>(reinterpret_cast<void*&>(method))) 

注意,這確實爲指向成員工作功能。 指向成員函數的指針是完全不同的野獸,我不認爲 認爲有任何有效的方式將它們用作這種方式的關鍵 (因爲它們可能並經常包含填充或未設置字段, 某些情況下)。

對於這個問題,在正式的情況下,即使對於正常的指針 也不能保證。該標準允許指針不必關心位,或者對幾個不同指針表示進行比較相等。然而,在 的實踐中,它在大多數(所有)現代機器上都是安全的。

+0

非常感謝詹姆斯 - 它既合理又有效:) – Martin 2012-08-16 08:34:52

2

你應該罰款寫作:

reinterpret_cast<uintptr_t>(method) 

[編輯] - 爲指針的方法,你將不得不留在C-風格的轉換,如解釋這個SO:reinterpret_cast to void* not working with function pointers

&方法爲你suspec指針的指針,所以它不是你想

什麼

uintptr_t的是在INT更好,因爲它是保證大小爲指針

+0

與**常規**指針的大小相同。成員函數的指針是不同的野獸。 – MSalters 2012-08-16 08:29:56

+0

謝謝,我試過這個,但編譯器說:'reinterpret_cast':不能從'void(__thiscall Object :: *)(int)'轉換爲'uintptr_t。 – Martin 2012-08-16 08:30:04

+0

@ MSalters他試圖做的僅僅是指向成員函數的指針,所以沒有必要討論它,除非指出不可能。他必須通過非會員(或靜態會員)功能。 (這是相當平凡的,因爲他有對象,他需要的只是一個關於對象類型和指向成員值的函數模​​板,並且每次都要實例化它。) – 2012-08-16 08:33:17

相關問題