在运行时创建函数并创建函数指针

4
我想做类似以下的事情:
for(int i=0;i<10;i++)
    addresses[i] = & function(){ callSomeFunction(i) };

基本上,拥有一个与数字列表相关的行为函数地址数组。
如果可以使用像Boost.Lambda这样的外部类,那就可以了。
编辑:经过一些讨论,我得出结论,我没有表达清楚。请阅读在运行时创建函数指针的函数

我最终真正想做的是:

class X
{
    void action();
}

X* objects;

for(int i=0;i<0xFFFF;i++)
    addresses[i] = & function(){ objects[i]->action() };


void someFunctionUnknownAtCompileTime()
{

}

void anotherFunctionUnknowAtCompileTime()
{

}

使用汇编补丁一些在编译时未知的函数,跳转到 addresses[0] 对应的函数。

使用汇编补丁另一些在编译时未知的函数,跳转到 addresses[1] 对应的函数。

有些问题,我认为你的方法行不通,因为它们不是真正的函数,但我的解释不够清楚,没有表达我想要做什么。


1
为什么不编辑原始问题以包含更新后的代码?这样我们就不必搜索添加的注释了。 - vividos
我在第一篇帖子的末尾发布了一个链接。我没有直接在那里发布代码,因为那是一种对话方式。 - user246100
7个回答

3
如果我理解正确,您正在尝试在运行时生成机器代码并将其填充到缓冲区中,并获取该代码的函数指针,以便您可以调用它。
这是可能的,但具有挑战性。您可以使用reinterpret_cast<>将数据指针转换为函数指针,但您需要确保为缓冲区分配的内存被操作系统标记为可执行。这将涉及一个系统调用(Windows上的LocalAlloc() iirc,Unix上无法记住)而不是“普通的”malloc/new调用。
假设您已经拥有可执行的内存块,您必须确保您的机器代码遵守您创建的函数指针所指示的调用约定。这意味着在函数开头推入/弹出适当的寄存器等。
但是,一旦您完成了这些步骤,您应该能够像任何其他函数一样使用您的函数指针。
值得一提的是,看一下开源JVM(或Mono)如何做到这一点可能会很有价值。这就是JIT编译的本质。

我认为你没有正确理解我的意思。我的意图是在编译时不知道的几个函数中使用汇编跳转进行补丁(Detour它们)。每个跳转应该指向与不同数字相关联的函数。编辑:再次阅读您的帖子,您理解的比我想象的要多。问题是:如何生成该机器代码? - user246100
等等,我想我明白了。我会尝试用代表函数的汇编指令组来填充内存,并提供它们的地址。 - user246100
@unknown:小心,你想要的不是汇编语言,而是机器码(即汇编器的输出,二进制机器码)。 - Drew Hall
我的有关成员函数指针的评论仍然适用。您需要两个信息 - 对象的地址和函数的地址(因为在C++中,对象只是包含其成员的一块内存块)。 - Igor Zevaka
Igor,我知道那个问题,但那不是问题所在。 Drew,是的,我起了个错误的名字。无论如何,问题已经解决并测试通过了,谢谢。 - user246100

2

这是我刚刚拼凑出来的一个例子:

int func1( int op )
{
   printf( "func1 %d\n", op );
   return 0;
}
int func2( int op )
{
   printf( "func2 %d\n", op );
   return 0;
}

typedef int (*fp)(int);


int main( int argc, char* argv[] )
{
    fp funcs[2] = { func1, func2 };
    int i;

    for ( i = 0; i < 2; i++ )
        {
        (*funcs[i])(i);
        }

}

好像忘记了一个数组索引:(*funcs[i])(i); - Drew Hall
是的确如此。感谢您指出这一点;我刚刚修复了它。有趣的是,我实际上编译并运行了它……但我看到的是我想要看到的,而不是实际打印出来的内容。 - Mark Wilkins

1
最简单的方法应该是创建一堆boost::function对象:
#include <boost/bind.hpp>
#include <boost/function.hpp>
// ...

std::vector< boost::function<void ()> > functors;
for (int i=0; i<10; i++)
   functors.push_back(boost::bind(callSomeFunction, i));

// call one of them:
functors[3]();

请注意,向量的元素不是“真实函数”,而是具有重载operator()的对象。通常这不应该是一个缺点,实际上比真实函数指针更容易处理。

0

这基本上就是上面所说的,但修改您的代码将类似于这样:

std::vector<int (*) (int)> addresses;
for(int i=0;i<10;i++) {
     addresses[i] = &myFunction;  
}

我并不是很清楚你所说的在运行时创建函数的意思...我认为你无法在运行时创建函数,但你可以在运行时将函数指针分配给数组/向量。请记住,使用这种方法时,所有的函数都需要具有相同的签名(相同的返回类型和参数)。

0

如果没有 this 指针,你无法单独调用成员函数。类的所有实例在内存中都有一个存储函数的位置。当你调用 p->Function() 时,p 的值被存储在某个地方(我记不清是寄存器还是堆栈),并且该值被用作基本偏移量来计算成员变量的位置。

因此,这意味着如果你想调用一个函数,你必须存储函数指针和对象指针。这个的一般形式可能是这样的:

class MyClass {
  void DoStuf();
};

//on the left hand side is a declaration of a member function in the class MyClass taking no parameters and returning void.
//on the right hand side we initialize the function pointer to DoStuff
void (MyClass::*pVoid)() = &MyClass::DoStuff;

MyClass* pMyClass = new MyClass();
//Here we have a pointer to MyClass and we call a function pointed to by pVoid.
pMyClass->pVoid();

谢谢,但那与此无关。 - user246100

0

你可以通过事先在全局范围内定义一些任意名称的函数来简单地实现这一点。


我想对最多0xFFFF个函数执行此操作。 - user246100

0

根据我的理解,您正在尝试在运行时创建函数(就像我们可以在Ruby中做的那样)。如果这是您的意图,恐怕在像C++这样的编译语言中是不可能的。 注意:如果我对问题的理解不正确,请不要投反对票:)


我希望某个库提供的lambda函数能够给我一个解决方案。是的,你理解得很正确。 - user246100

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接