2012-08-16 62 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

相关问题