18

私は現在GCC4.4を使用してvoid*いますが、メンバー関数へのポインターとの間でかなりの頭痛の種があります。次のように、C++オブジェクトをLuaインタープリターにバインドするための使いやすいライブラリを作成しようとしています。

LuaObject<Foo> lobj = registerObject(L, "foo", fooObject);
lobj.addField(L, "bar", &Foo::bar);

次の関数(一般化する機会が得られるまで特定の関数シグネチャに固有)を除いて、ほとんどの作業を完了しました。

template <class T>
int call_int_function(lua_State *L) 
{
    // this next line is problematic
    void (T::*method)(int, int) = reinterpret_cast<void (T::*)(int, int)>(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast<T *>(lua_touserdata(L, 1));

    (obj->*method)(lua_tointeger(L, 2), lua_tointeger(L, 3));
    return 0;
}

Luaに慣れていない方のためにlua_touserdata(L, lua_upvalueindex(1))、クロージャに関連付けられた最初の値(この場合はメンバー関数へのポインター)を取得し、それを。として返しますvoid*。GCCは、void*->void (T::*)(int, int)が無効なキャストであると文句を言います。これを回避する方法について何かアイデアはありますか?

4

5 に答える 5

25

メンバーへのポインターを他の「通常の」ポインター型にキャストするvoid *ことはできません。メンバーへのポインターは、通常のポインターのようなアドレスではありません。最も可能性が高いのは、メンバー関数を通常の関数でラップすることです。C++ FAQ Liteでは、これについて詳しく説明しています。主な問題は、メンバーへのポインターを実装するために必要なデータが単なるアドレスではなく、実際にはコンパイラーの実装によって大きく異なることです。

lua_touserdataユーザーデータが返すものを制御できると思います。この情報を取り戻す合法的な方法がないため、メンバーへのポインタにすることはできません。ただし、他にもいくつかの選択肢があります。

  • おそらく最も簡単な選択は、メンバー関数をフリー関数でラップして返すことです。その自由な関数は、オブジェクトを最初の引数として受け取る必要があります。以下のコードサンプルを参照してください。

  • Boost.Bind の mem_funと同様の手法を使用して、適切にテンプレート化できる関数オブジェクトを返します。これが簡単だとは思いませんが、必要に応じて、より多くの状態を関数の戻り値に関連付けることができます。

最初の方法を使用して関数を書き直すと、次のようになります。

template <class T>
int call_int_function(lua_State *L) 
{
    void (*method)(T*, int, int) = reinterpret_cast<void (*)(T*, int, int)>(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast<T *>(lua_touserdata(L, 1));

   method(obj, lua_tointeger(L, 2), lua_tointeger(L, 3));
   return 0;
}
于 2009-08-20T16:30:43.880 に答える
10

ユニオンを使用して、ポインターをメンバー関数と属性に変換できます。

// helper union to cast pointer to member
template<typename classT, typename memberT>
union u_ptm_cast {
    memberT classT::*pmember;
    void *pvoid;
};

変換するには、ソース値を 1 つのメンバーに入れ、ターゲット値を別のメンバーから取り出します。

この方法は実用的ですが、すべての場合にうまくいくかどうかはわかりません。

于 2011-08-21T16:53:19.897 に答える
3

ここで、関数 void_cast のパラメーターを必要に応じて変更するだけです。

template<typename T, typename R>
void* void_cast(R(T::*f)())
{
    union
    {
        R(T::*pf)();
        void* p;
    };
    pf = f;
    return p;
}

使用例:

auto pvoid = void_cast(&Foo::foo);
于 2016-05-28T18:57:16.073 に答える
1

メンバー関数へのポインターをキャストする際の制限を考慮した回避策として、void*関数ポインターをヒープに割り当てられた小さな構造体でラップし、その構造体へのポインターをLuaユーザーデータに配置できます。

template <typename T>
struct LuaUserData {
    typename void (T::*MemberProc)(int, int);

    explicit LuaUserData(MemberProc proc) :
        mProc(proc)
    { }

    MemberProc mProc;
};

LuaObject<Foo> lobj = registerObject(L, "foo", fooObject);
LuaUserData<Foo>* lobj_data = new LuaUserData<Foo>(&Foo::bar);

lobj.addField(L, "bar", lobj_data);

// ...

template <class T>
int call_int_function(lua_State *L) 
{
    typedef LuaUserData<T>                       LuaUserDataType;
    typedef typename LuaUserDataType::MemberProc ProcType;

    // this next line is problematic
    LuaUserDataType* data =
        reinterpret_cast<LuaUserDataType*>(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast<T *>(lua_touserdata(L, 1));

    (obj->*(data.mMemberProc))(lua_tointeger(L, 2), lua_tointeger(L, 3));
    return 0;
}

私はLuaに精通していないので、上記の例で何かを見落としている可能性があります。このルートを使用する場合は、LuaUserDataの割り当てを管理する必要があることにも注意してください。

于 2009-08-20T16:35:33.417 に答える
1

複雑な表現を持つポインターからメンバーへの型である非静的メンバー関数のアドレスとは異なり、静的メンバー関数のアドレスは通常、 への変換と互換性のある単なるマシン アドレスvoid *です。

C++ の非静的メンバ関数を に基づく C または C のようなコールバック メカニズムにバインドする必要がある場合はvoid *、代わりに静的ラッパーを作成することができます。

ラッパーは、インスタンスへのポインターを引数として取り、非静的メンバー関数に制御を渡すことができます。

void myclass::static_fun(myclass *instance, int arg)
{
   instance->nonstatic_fun(arg);
}
于 2016-01-06T05:09:23.400 に答える