指针指向成员的函数指针

时间:2016-03-20 14:05:17

标签: c++ member-function-pointers

之前已经提出过,但问题略有不同,答案都非常无益,所以我再试一次。

我需要来自编译器的2条信息,这些信息似乎难以提取。

  1. 我想在没有类的实例的情况下找到给定类的vtable指针。我可以在二进制文件中的任何地方找到vtable符号的唯一引用是在它被分配给新实例的构造函数中,并且在没有调用它的情况下将指针移出构造函数真的很尴尬。我想知道是否有人可以指出常见编译器(msvc,gcc,clang)的vtable名称修改规范,所以我可以明确地对符号进行外设吗? (我还没有找到这个)。我对此的担心是,我怀疑(至少在VC上)符号名称中有一些字符在C ++标识符中是非法的,因此我不确定如何创建链接到它的变量...

  2. 我需要方法的实际函数指针。接近成员函数指针似乎唯一可用的语法是指向成员的运算符,这会产生非常符合编译器的输出。

    我观察到,GCC / Clang产生了一个漂亮的小结构; {void * ptr_or_offset; size_t susp_vtable; }。从那里,它很容易找到实际的函数指针(假设我有vtable指针!见#1)。

    MSVC有点困难;指向虚拟成员的指针是指向执行虚拟查找的thunk函数的指针。似乎thunk与vtable偏移量相关,因此每个vtable偏移量都有一个thunk。这种策略很难;识别方法是否为虚拟,如果是,则获取vtable偏移量(因此得到实际的函数指针)。我想也许我可以为每个vtable偏移量制作一个thunk指针表,直到N,然后当我拿一个指向成员的指针时,我可以将它与thunk表中的每个项目进行比较;如果它是其中之一,我知道它是虚拟的,并且vtable偏移,所以我可以得到指针。

  3. 所以,这一切听起来都很可怕,但它就是这样,因为C ++并不觉得语法应该可用于获取这些基本语言原语,并且由于某些未知原因而没有合适的代表!

    有人能想到更好或更直接的机制来捕获我寻求的那些数据吗?或者任何可提高便携性的替代解决方案也会很酷!

    干杯!

    编辑:考虑到其他类似的帖子已经充满了人们说“它不便携”,并且“不做”#39;我想请求你不要用更多相同的东西污染这个线程。他们是毫无价值的评论,没有解决这个问题。这个问题需要一些创造性思维,让我们对解决方案的质量留下深刻印象。

    编辑2:不确定我为什么被投票。这是一个有趣且很难解决的问题。关于互联网的讨论非常少。

3 个答案:

答案 0 :(得分:0)

虚拟表根本不是c ++标准的全部内容。在整个ISO标准中没有关于它的说法。它只是一种常用的实施方法。这就是为什么您找不到工具支持来做您想做的事情。

此外,虚拟表可能是一个非常复杂的问题,例如在多重继承的情况下,在同一对象中使用几个不同的虚拟表(对于不同的子对象)。

你已经找到了一种精确的方式来获取一些信息,并且显然已经详细研究了这一点。但是,我不确定你找到vtable偏移的方法是否适用于所有不同的条件。我强烈建议你考虑一下你的问题并找到另一种方法来实现它(除非你正在研究新的调试工具并且别无选择;-))

答案 1 :(得分:0)

这只是一个提示,并且不确定它是否与您实际问题相关。

如果您需要访问底层的vTable(这只是实现细节),您应该考虑显式的vTable管理。这意味着所有动态类都应该没有虚函数,而只是指向显式vtable的指针,该指针包含指向函数(而不是成员函数)的指针,其第一个参数应该是指向该类对象的指针(或ref)。 p>

然后,您应该明确地使用invoke函数或宏,该函数或宏稍后会调用类似inner_invocation(object, offset_of_method, other_args...)的内容。一旦你到那里就可以手动调用 实际的虚拟成员函数

好吧,它看起来真的像C中的C ++,但至少它可以在不过多依赖编译器实现细节的情况下掌握操作。

再次阅读您的问题和评论之后,这是一个简单的方法来进行动态推导。这就是Java程序员所称的代理:代理(Java sense)是一个瘦实例,它在真实对象上实现 interface (仅包含纯虚方法的类)。好的,Java提供了创建代理的所有机制,而C ++并不是那么友好,但可扩展性足以提供像它一样构建一些思路的方法。

你应该忘记vtables并将它们视为接口 - 顺便说一句,你获得了可移植性。

以下示例说明如何在类上实现两个接口:

#include <iostream>
#include <string>
#include <sstream>

// definition of template class proxy
template<class C, class ... I>
class Proxy: public C, public I... {
private:
    Proxy() {};
public:
    Proxy(const C& c): C(c) {};   // uses copy constructor on original object
    virtual ~Proxy() {};
};

/* if the proxy is not required to inherit from C, you can take a ref    
template<class C, class ... I>
class Proxy: public C, public I... {
private:
    C& obj;
    Proxy() {};
public:
    Proxy(const C& c): obj(c) {};   // just copy the reference
    virtual ~Proxy() {};
};
*/
// macro definition to help in proxy declarations
#define BEGIN_DECLARE_PROXY(proxy, cls, ...) \
class proxy: public Proxy<cls, __VA_ARGS__> { \
public: \
    proxy(const C& c): Proxy(c) {}; \

#define IMPLEMENT0(type, method, function) \
    type method() { \
        return function(*this ); \
    }
#define IMPLEMENT1(type, method, function, typ1, arg1) \
    type method(typ1 arg1) { \
        return function(*this, arg1 ); \
    }
#define IMPLEMENT2(type, method, function, typ1, arg1, typ2, arg2) \
    type method(typ1 arg1, typ2 arg2) { \
        return function(*this, arg1, arg2); \
    }
#define IMPLEMENT3(type, method, function, typ1, arg1, typ2, arg2, typ3, arg3) \
    type method(typ1 arg1, typ2 arg2, typ3, arg3) { \
        return function(*this, arg1, arg2, arg3); \
    }
// could add many others - could not use VA_ARGS here ...
/* if we used the ref. version, implementation should be return function(obj, ...); */

#define END_DECLARE_PROXY };

// example actual class
struct C {
    int val;
    std::string name;
};

// example interfaces
class I {
public:
    virtual std::string display() = 0;
};

class I2 {
public:
    virtual void show(std::ostream& out) = 0;
};

// function implementing the interfaces
std::string C_display(const C& c) {
    std::stringstream ss;
    ss << c.name << " (" << c.val << ")";
    return ss.str();
}

void C_show(const C& c, std::ostream& out) {
    out << C_display(c) << std::endl;
}

// actual proxy definition
BEGIN_DECLARE_PROXY(CI, C, I, I2)
IMPLEMENT0(std::string, display, C_display)
IMPLEMENT1(void, show, C_show, std::ostream&, out)
END_DECLARE_PROXY

int main() {
    C c = {12, "Foo"}; // create an object
    CI ci(c);   // build a proxy around the object
    I& i = ci;  // an interface on the proxy

    // example calls
    std::cout << i.display() << std::endl;
    ci.show(std::cout);

    return 0;
}

答案 2 :(得分:0)

我一直在努力解决这个问题,我已经将它用于lib:https://github.com/TurkeyMan/virtualwrangler

完成了99%,我只需要一个GCC实现:

template <typename C>
inline VTable GetVTable();

有人能想出任何创造性的方法来强迫GCC为某些班级制作vtable ptr吗? 我最好的是:

extern "C" void *_ZTV7MyClass;

哪个没关系,除了符号名称中有数字'7',这是该类名称的字符串长度!!这意味着我无法将该声明用于宏...除非有一些聪明的方法来执行预处理器stringlen ...?

想法?