C++ преобразование указателя функции в уникальный" хэш " ключ
Se оригинальный вопрос внизу.
Я думаю, что понимаю, что вы, ребята, сейчас говорите – что, поскольку внутренняя структура указателя функции-члена является компилятором/машиной, это действительно невозможно сделать, что я пытаюсь. Поэтому, даже если он работает, когда я его тестирую – у меня нет гарантии, что он будет работать на других компиляторах/машинах.
Есть ли другой способ сделать то, что я хочу тогда?У меня есть класс шаблона и базовый класс шаблона для этого класса, и у меня есть класс делегата, который содержит std:: карту всех событий, которые класс делегата должен вызывать, когда он вызывается.
Причина, по которой мне нужна карта, заключается как в том, чтобы гарантировать, что одна и та же функция-член (событие, указывающее на функцию-член) не добавляется больше одного раза, так и в том, чтобы сделать возможным удаление событий из карты с помощью объекта и функции-члена, первоначально использовавшихся при создании экземпляра объекта-события.
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)))
.
Итак, мой вопрос - как я могу получить уникальный ключ от указателя функции, который будет таким же, если я позже получу ключ от другого указателя функции, указывающего тот же метод?
Заранее спасибо, Мартин
2 ответа:
Второе не является законным: формально, вы не можете преобразовать указатель в функция для указателя на данные (а
void*
- указатель на данные). Кроме того, вы не гарантируете, что сможете преобразовать любой указатель вint
; преобразование является законным только в том случае, еслиint
по крайней мере так же велико, как указатель (что означает, что ваш код не сможет скомпилироваться на большинстве 64 битных систем).Существует несколько способов решения этой. Во-первых, на большинстве (всех?) современный машины, указатели на функции и указатели на данные делать имеют одинаковые размер и представление. (На самом деле Posix этого требует. Даже если это это было не так на первых машинах Unix, которые я использовал.) Если мы предположим, что это, вы можете гарантировать достаточно большой интегральный тип, используя
intptr_t
, и "трюк" компилятора с использованием дополнительного уровня косвенности:std::pair<intptr_t, intptr_t>( reinterpret_cast<intptr_t>( reinterpret_cast<void*&>( object ) ), reinterpret_cast<intptr_t>( reinterpret_cast<void*&>( method ) ) )
(это предполагает, что
Обратите внимание, что это не работает для указателей на функции-члены. Указатель к функциям-членам относятся совершенно разные звери, а я нет думайте, что есть какой-то эффективный способ использовать их в качестве ключа таким образом (поскольку они могут содержать и часто содержат заполняющие или неназначенные поля, в некоторые случаи).object
иmethod
являются вашими указателями на объект и функция.)Если на то пошло, формально это не гарантируется даже для нормальных людей. указатели. Стандарт допускает указатели, чтобы не волнует, биты, или несколько различных представлений указателей для сравнения равны. В практика, однако, безопасна на большинстве (всех?) современный машины.
Вы должны хорошо писать:
reinterpret_cast<uintptr_t>(method)
[edit] - для указателей на методы вам придется остаться с приведениями в стиле c, как объясняется в этом так: reinterpret_cast to void * не работает с указателями функций
&метод, как вы подозреваете указатель на указатель, так что это не то, что вы хотите
Uintptr_t лучше, чем int, потому что он гарантированно будет иметь тот же размер, что и указатель