簡體   English   中英

從功能結構成員獲取功能地址

[英]Get function address from a function structure member

我正在嘗試獲取隱藏在結構后面的函數地址。 不幸的是, void*基本的C ++轉換void* ,因此我改用C++ template

1.基本 void* C ++轉換不適用於結構內部的函數,為什么?

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 ,我必須為每個參數類型集創建一個新模板。

原稿(確定):

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;

它比template好得多。 而且我想實現這個目標:

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

這可能嗎?

指向成員函數的指針與指向全局函數或靜態成員函數的指針完全不同。 造成這種情況的原因很多,但我不確定您對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純屬偶然。

原因是成員函數指針經常需要在其中存儲額外的數據,這些數據與修復傳遞的對象指針有關,以便對所調用的函數正確。 在我的示例中,當在Qux對象上調用Bar::addThemAll (由於繼承是完全有效的)時,需要在調用函數之前將指向Qux對象的指針調整為指向Bar子對象。 因此,成員函數的Qux::*必須在其中編碼此調整。 畢竟,說func = &Qux::addAllTheThings是完全有效的,並且如果調用了該函數,則無需進行指針調整。 因此,指針調整是函數指針值的一部分。

那只是一個例子。 允許編譯器以其認為合適的任何方式(在某些約束范圍內)實現成員函數指針。 許多編譯器(例如我在使用的64位平台上的GNU C ++編譯器)將以不允許將任何成員函數指針都視為等同於正常函數指針的方式來實現它們。

有一些方法可以解決這個問題。 處理成員函數指針的瑞士軍刀是C ++ 11或C ++ TR1中的::std::function模板。

一個例子:

 #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方法。 然后,您可以派生用於不同語法元素的類,例如加法表達式,變量或常量。 他們每個人都將針對其特定情況重載評估方法。 然后,您可以構建表達式樹。

雖然不知道細節,但這只是關於您設計的模糊建議。

這是一個干凈的解決方案。 通過模板將您的成員函數包裝為靜態成員函數。 然后,您可以將其轉換為所需的任何指針:

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);
}

看來您需要將指向成員函數的指針轉換為void *。 我假設您想將該指針作為“用戶數據”提供給某些庫函數,然后您將取回指針並希望在某些給定對象上使用它。

如果是這種情況,那么reinterpret_cast<void *>(...)可能是正確的事情...我假設接收指針的庫未使用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM