如何获得"直接"函数指针指向虚拟成员函数?

时间:2013-12-11 13:42:22

标签: c++ pointers

我正在开发一个嵌入式平台,它不能很好地处理动态代码(根本没有推测/ OOO执行)。 在这个平台上,我经常在同一个对象上调用一个虚拟成员函数,但编译器无法优化vtable-lookup,因为它似乎不认识只有第一次调用才需要查找。

因此我想知道:是否有一种手动方式来虚拟化C ++类的虚拟成员函数,以获得直接指向已解析地址的函数指针? 我看了一下C ++函数指针,但由于它们似乎需要指定一个类型,我想这不会有用。

提前谢谢

2 个答案:

答案 0 :(得分:2)

没有通用的标准-C ++ - 只能找到虚函数的地址,只给出对基类对象的引用。此外,没有合理的类型,因为this不需要作为普通参数传递,遵循一般约定(例如,它可以在寄存器中传递,其他args on栈)。

但是,如果您不需要可移植性,则可以随时执行适合您的编译器的任何操作。例如,使用Microsoft的COM(我知道,这不是您的平台),有一个已知的内存布局,带有vtable指针,以便从C访问功能。

如果您确实需要便携性,那么我建议设计优化。例如,而不是

class Foo_base
{
public:
    virtual void bar() = 0;
};

喜欢

class Foo_base
{
public:
    typedef (*Bar_func)(Foo_base&);

    virtual Bar_func bar_func() const = 0;
    void bar() { bar_func()( *this ); }
};

支持与以前相同的公共界面,但现在可以暴露内部,从而允许手动优化对bar的重复调用。

答案 1 :(得分:1)

关于gcc我在调试编译的汇编代码时看到了以下内容。 我已经看到泛型方法指针包含两个数据: a)该方法的“指针” b)最终添加到类实例起始地址的偏移量(当涉及多个继承时使用偏移量,对于第二个和更多父类的方法,如果应用于它们的对象,则将其数据放在不同的起始点)。< / p>

该方法的“指针”如下: 1)如果“指针”是偶数,则将其解释为正常(非虚拟)函数指针。 2)如果“指针”为奇数,则应减去1,剩余值应为0或4或8或12(假设指针大小为4字节)。 之前的编码显然假设所有正常方法都从偶数地址开始(因此编译器应该在偶数地址处对齐它们)。 因此,offset是vtable的偏移量,用于获取“真实”非虚方法指针的地址。

因此,为了对调用进行虚拟化,正确的想法是将虚方法指针转换为非虚方法指针,然后使用它以将其应用于作为我们类实例的“主题”。

下面的代码执行了所描述的内容。

#include <stdio.h>
#include <string.h>
#include <typeinfo>
#include <typeindex>
#include <cstdint>

struct Animal{
    int weight=0x11111111;
    virtual int mm(){printf("Animal1 mm\n");return 0x77;};
    virtual int nn(){printf("Animal1 nn\n");return 0x99;};
};

struct Tiger:Animal{
    int weight=0x22222222,height=0x33333333; 
    virtual int mm(){printf("Tigerxx\n");return 0xCC;}
    virtual int nn(){printf("Tigerxx\n");return 0x99;};
};

typedef int (Animal::*methodPointerT)();

typedef struct {
    void** functionPtr;
    size_t offset;
} MP;

void devirtualize(methodPointerT& mp0,const Animal& a){
    MP& t=*(MP*)&mp0;
    if((intptr_t)t.functionPtr & 1){
        size_t index=(t.functionPtr-(void**)1); // there is obviously a more
        void** vTable=(void**)(*(void**)&a);        // efficient way. Just for clearness !
        t.functionPtr=(void**)vTable[index];
    }
};

int main()  
{
    int (Animal::*mp1)()=&Animal::nn;
    MP& mp1MP=*(MP*)&mp1;

  Animal x;Tiger y;

    (x.*mp1)();(y.*mp1)();

    devirtualize(mp1,x);

    (x.*mp1)();(y.*mp1)();

}