1

構造体の背後に隠されている関数アドレスを取得しようとしています。残念ながら、void*基本的なC ++変換は機能しないため、C++ template代わりに使用しました。

1.基本的なC++変換は、構造内の関数では機能しません。なぜですか?void*

void * lpfunction;
lpfunction = scanf; //OK
lpfunction = MessageBoxA; //OK

私は単純な構造を作りました:

struct FOO{

    void PRINT(void){printf("bla bla bla");}

    void SETA(int){} //nothing you can see
    void SETB(int){} //nothing you can see

    int GETA(void){} //nothing you can see
    int GETB(void){} //nothing you can see
};
///////////////////////////////////////////
void *lpFunction = FOO::PRINT;

そしてコンパイルエラー:

error C2440: 'initializing' : 
cannot convert from 'void (__thiscall FOO::*)(void)' to 'void *'

2.機能メンバーのアドレスを取得することは不可能ですか?

次に、関数メンバーをアドレスに変換できるテンプレート関数を作成しました。それでは組み立てで呼びます。次のようになります。

template <class F,void (F::*Function)()>  
void * GetFunctionAddress() {

    union ADDRESS  
    { 
        void (F::*func)();  
        void * lpdata;  
    }address_data;  

    address_data.func = Function;  
    return address_data.lpdata; //Address found!!!  

}  

そしてここにコードがあります:

int main()
{
    void * address = GetFunctionAddress<FOO,&FOO::PRINT>();

    FOO number;
    number.PRINT(); //Template call
    void * lpdata = &number;

    __asm mov ecx, lpdata //Attach "number" structure address
    __asm call address //Call FOO::PRINT with assembly using __thiscall

printf("Done.\n");
system("pause");
return 0;
}

しかし、それは非常に具体的だと思います。LOCK-KEYのように見え、引数タイプのセットごとに新しいテンプレートを作成する必要があります。

オリジナル(OK):

void PRINT(); //void FOO::PRINT();

少し変更します:

void PRINT(int); //void FOO::PRINT(int);

古いテンプレートコードですぐに、コンパイラは次のように表示します。

//void (F::*func)();
//address_data.func = Function;

error C2440: '=' : cannot convert from 
'void (__thiscall FOO::*)(int)' to 'void (__thiscall FOO::*)(void)'

なんで?それらは単なるアドレスです。

69:       address_data.func = Function;
00420328  mov  dword ptr [ebp-4],offset @ILT+2940(FOO::PRINT) (00401b81)

..。

EDIT3:私はより良い解決策を知っています:

void(NUMBER::*address_PRINT)(void) = FOO::PRINT;
int(NUMBER::*address_GETA)(void) = FOO::GETA;
int(NUMBER::*address_GETB)(void) = FOO::GETB;
void(NUMBER::*address_SETA)(int) = FOO::SETA;
void(NUMBER::*address_SETA)(int) = FOO::SETB;

テンプレートよりもはるかに優れています。ちなみに私は目標を達成したいです:

<special_definition> lpfunction;
lpfunction = FOO::PRINT; //OK
lpfunction = FOO::GETA; //OK
lpfunction = FOO::GETB; //OK
lpfunction = FOO::SETA; //OK
lpfunction = FOO::SETB; //OK

これは可能ですか?

4

3 に答える 3

6

メンバー関数へのポインターは、グローバル関数または静的メンバー関数へのポインターのようなものではありません。これには多くの理由がありますが、C ++がどのように機能するかについてあなたがどれだけ知っているかわからないので、どのような理由が理にかなっているのかわかりません。

アセンブリで試みていることは、一般的なケースでは機能しないことを私は知っています。メンバー関数と関数ポインタの目的について根本的な誤解があるようです。

重要なのは、C++では通常実行しないいくつかのことを実行しているということです。通常、C ++で関数ポインタのテーブルを作成することはありません。そのようなものを使用するのは、virtual関数の目的であるためです。

このアプローチを使用することにした場合は、C ++をまったく使用せず、Cのみを使用することをお勧めします。

これらのポインタタイプが完全に互換性がないことを証明するために、次のプログラムがあります。

#include <cstdio>

struct Foo {
   int a;
   int b;
   int addThem() { return a + b; }
};

struct Bar {
   int c;
   int d;
   int addThemAll() { return c + d; }
};

struct Qux : public Foo, public Bar {
   int e;
   int addAllTheThings() { return Foo::addThem() + Bar::addThemAll() + e; }
};

int addThemGlobal(Foo *foo)
{
   return foo->a + foo->b;
}

int main()
{
   int (Qux::*func)();

   func = &Bar::addThemAll;
   printf("sizeof(Foo::addThem) == %u\n", sizeof(&Foo::addThem));
   printf("sizeof(Bar::addThemAll) == %u\n", sizeof(&Bar::addThemAll));
   printf("sizeof(Qux::addAllTheThings) == %u\n", sizeof(&Qux::addAllTheThings));
   printf("sizeof(func) == %u\n", sizeof(func));
   printf("sizeof(addThemGlobal) == %u\n", sizeof(&addThemGlobal));
   printf("sizeof(void *) == %u\n", sizeof(void *));
   return 0;
}

私のシステムでは、このプログラムは次の結果をもたらします。

$ /tmp/a.out 
sizeof(Foo::addThem) == 16
sizeof(Bar::addThemAll) == 16
sizeof(Qux::addAllTheThings) == 16
sizeof(func) == 16
sizeof(addThemGlobal) == 8
sizeof(void *) == 8

メンバー関数ポインターの長さが16バイトであることに注意してください。には収まりませんvoid *。通常の意味でのポインタではありません。あなたのコードとunion純粋に偶然に動作します。

この理由は、メンバー関数ポインターは、呼び出される関数を正しくするために、渡されるオブジェクトポインターの修正に関連して、メンバー関数ポインターに追加のデータを格納する必要がある場合が多いためです。私の例では、オブジェクトで呼び出さBar::addThemAllれたQux場合(継承のために完全に有効です)、関数が呼び出される前に、オブジェクトへのポインターをサブオブジェクトQuxを指すように調整する必要があります。Barしたがって、Qux::*s to member関数には、この調整がエンコードされている必要があります。結局のところ、言うことfunc = &Qux::addAllTheThingsは完全に有効であり、その関数が呼び出された場合、ポインターの調整は必要ありません。したがって、ポインター調整は関数ポインターの値の一部です。

そして、それはほんの一例です。コンパイラーは、(特定の制約内で)適切と思われる方法でメンバー関数ポインターを実装することが許可されています。多くのコンパイラー(私が使用していた64ビットプラットフォーム上のGNU C ++コンパイラーなど)は、メンバー関数ポインターが通常の関数ポインターとまったく同等に扱われないように実装します。

これに対処する方法があります。メンバー関数ポインターを処理するスイスアーミーナイフは::std::function、C++11またはC++TR1のテンプレートです。

例:

 #include <functional>

 // .... inside main
    ::std::function<int(Qux *)> funcob = func;

funcob関数のように呼び出すことができ、を必要とするものはすべて絶対に指すことができますQux *。メンバー関数、グローバル関数、静的メンバー関数、ファンクター...funcobはそれを指すことができます。

ただし、この例はC++11コンパイラでのみ機能します。ただし、コンパイラがかなり最近のものであるが、それでもC ++ 11コンパイラではない場合は、代わりにこれが機能する可能性があります。

 #include <tr1/functional>

 // .... inside main
    ::std::tr1::function<int(Qux *)> funcob = func;

悪化した場合は、この概念全体の元となったBoostライブラリを使用できます。

しかし、私はあなたのデザインを再考します。よく考えられた継承階層を持ち、virtual関数を使用することで、現在行っていることよりもはるかに多くのマイレージが得られると思います。インタプリタを使用すると、評価可能なあらゆるものの抽象クラスである最上位の抽象「式」クラスができます。私はそれに仮想evaluateメソッドを与えます。次に、加算式、変数、定数など、さまざまな構文要素のクラスを導出できます。それらのそれぞれは、特定のケースの評価メソッドをオーバーロードします。次に、式ツリーを構築できます。

詳細はわかりませんが、それはあなたのデザインについての漠然とした提案にすぎません。

于 2013-01-27T09:35:02.857 に答える
2

これがクリーンなソリューションです。テンプレートを使用して、メンバー関数を静的メンバー関数にラップします。次に、それを任意のポインタに変換できます。

template<class F, void (F::*funct)()>
struct Helper: public T {
  static void static_f(F *obj) {
    ((*obj).*funct)();
  };
};

struct T {
  void f() {
  }
};

int main() {
  void (*ptr)(T*);
  ptr = &(Helper<T,&T::f>::static_f);
}
于 2013-01-27T10:17:30.610 に答える
-1

メンバー関数へのポインタをvoid*に変換する必要があるようです。そのポインタをライブラリ関数への「ユーザーデータ」として指定すると、ポインタが返され、特定のオブジェクトで使用したいと思います。

この場合、areinterpret_cast<void *>(...)は正しいことかもしれません...ポインタを受け取っているライブラリはそれを使用していないと思います。

于 2013-01-27T09:48:50.993 に答える