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,我必须为每组参数类型制作一个新模板。

原件(好的):

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::addThemAllQux对象上调用时(由于继承而完全有效),指向Qux对象的指针需要在调用函数之前调整为指向Bar子对象。因此Qux::*,成员函数的 s 必须在其中编码这种调整。毕竟,sayingfunc = &Qux::addAllTheThings是完全有效的,如果调用该函数,则无需调整指针。所以指针调整是函数指针值的一部分。

这只是一个例子。允许编译器以他们认为合适的任何方式(在某些约束内)实现成员函数指针。许多编译器(比如我使用的 64 位平台上的 GNU C++ 编译器)将以不允许任何成员函数指针被视为完全等同于普通函数指针的方式实现它们。

有办法解决这个问题。处理成员函数指针的瑞士军刀是::std::functionC++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 回答