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, потому что он гарантированно будет иметь тот же размер, что и указатель