2

下部にある元の質問を参照してください。

私はあなたが今言っていることを理解していると思います.メンバー関数ポインタの内部構造はコンパイラ/マシン固有であるため、私がしようとしていることは実際には不可能です. そのため、テストすると機能しますが、他のコンパイラ/マシンで機能するという保証はありません.

私が望むことについて別の方法はありますか?

テンプレート クラスとそのクラスの基本テンプレート クラスがあり、デリゲート クラスが呼び出されたときに呼び出す必要があるすべてのイベントの 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、メソッドのオブジェクトへのポインターです。

それは機能しますが、キーの 2 番目の部分を取得する方法が完全に正しくないという卑劣な疑いがあります。

私は関数ポインタを完全に理解したことがありませんが、関数のアドレスではなくポインタのアドレスを取得していると思います.コンパイラは私にこのようなことをさせません((int)(static_cast<const void*>(method))).

だから私の質問は、関数ポインタから一意のキーを取得するにはどうすればよいですか?同じメソッドを指す別の関数ポインタからキーを後で取得した場合も同じです。

前もってありがとう、マーティン

4

2 に答える 2

4

2つ目は合法ではありません。正式には、関数へのポインターをデータへのポインターに変換することはできません(avoid*はデータへのポインターです)。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 ) ) )

(これは、objectmethodがオブジェクトと関数へのポインターであることを前提としています。)

これは、メンバー関数へのポインターでは機能しないことに注意してください。メンバー関数へのポインターは完全に異なる獣であり、このようにそれらをキーとして使用する効果的な方法はないと思います(場合によっては、パディングまたは未設定のフィールドが含まれている可能性があり、多くの場合、含まれているため) 。

さらに言えば、正式には、これは通常のポインターに対しても実際には保証されていません。この標準では、ポインターがドントケアビットを持つこと、またはいくつかの異なるポインター表現が等しく比較されることを許可しています。ただし、実際には、ほとんどの(すべて?)最新のマシンで安全です。

于 2012-08-16T08:30:54.160 に答える
2

あなたはうまく書いているはずです:

reinterpret_cast<uintptr_t>(method)

[編集] - メソッドへのポインターについては、この SO で説明されているように、c スタイルのキャストを使用する必要があります。

&method は、ポインターからポインターへの疑いがあるため、必要なものではありません

uintptr_t は、ポインターと同じサイズであることが保証されているため、int よりも優れています。

于 2012-08-16T08:21:43.047 に答える