获取成员函数内的成员函数的地址

时间:2014-08-29 08:46:18

标签: c++

我想在同一个成员函数中获取成员函数的地址。已经获取了这个指针,所以我知道从哪个对象调用。当然编译器抱怨我正在尝试使用指向此函数的指针而不调用它。我看到将这个规则放在一个对象之外的重点。有没有办法让“我自己”(函数在被调用时解决)地址?

这仅用于调试目的,应该在宏中使用。有办法做到这一点真是太好了。

澄清的代码:

class A
{
    void do_stuff()
    {
        printf("0x%X\n", (void*)(this->do_stuff));
    }
};

这是在嵌入式系统上我不明白为什么绝对没有办法获得do_stuff在flash中保存的地址。

编辑我尝试获取“指向成员函数的指针”,如下所示:

printf("CAPP::TASK 0x%08X\n", &CApp::Start);

它给了我0x08013E85,我在链接器映射中查找它,它正是我正在寻找的功能。这是使用KeilμVision5作为编译器的mdk-arm。这是对的吗?

编辑2: 各种类型转换都给我无效的类型转换。虽然printf的输出是正确的:Printf使用可变参数宏。让我们重建一下:

uint32_t getAdr(uint32_t n, ...)
{
    uint32_t adr=0;
    va_list vl;
    va_start(vl,n);
    adr=va_arg(vl,uint32_t);
    va_end(vl);
    return adr;
}

#define GetAdr(x) getAdr(0,&x);

void CApp::Start()
{  
    uint32_t a = GetAdr(CApp::Start);
    printf("Adr: 0x%08X\n", a);
    ...
}

令人讨厌的黑客攻击,但它确实奏效了。但为什么这有效呢?可悲的是,我没有__va_arg(...)的实现。 __va_arg如何对给定类型进行类型转换?有没有办法以某种方式使用该功能?

2 个答案:

答案 0 :(得分:3)

指向成员的指针函数通常是普通指针大小的两倍,如void*,因此标准C ++中无法直接转换。

https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html记录了一个GCC扩展,它允许您通过组合this&CApp::Start来获取正常(非成员)函数指针(该页面还解释了为什么指向成员函数的指针)除了函数地址之外还有其他信息。)

可以像这样使用扩展名:

typedef void (*pf_type)(CApp*);
pf_type pf = (pf_type)&CApp::Start;

我猜你的va_arg hack是有效的,因为你的编译器将指向成员函数的指针&CApp::Start表示为双字对象,其中一个单词中的函数地址为你通过varargs hack阅读,因为两个单词被推入堆栈然后你只读了其中一个。您可以使用union获得类似的结果:

union dirty_hack
{
  void (CApp::*pmf)();
  uint32_t words[2];
};
dirty_hack x;
x.pmf = &CApp::Start;
printf("Adr: 0x%08X\n", x.words[0]);   // might need to be x.words[1] instead

您可以使用模板来概括:

template<typename F>
uint32_t
getaddr(F f)
{
  union dirty_hack
  {
    F pmf;
    uint32_t words[2];
  };
  dirty_hack x;
  x.pmf = f;
  return x.words[0];   // might need to be x.words[1] instead
}

printf("Adr: 0x%08X\n", getaddr(&CApp::Start));

N.B。最好使用uintptr_t而不是int32_t来实现指针不是32位的系统的可移植性,尽管这种hackery本质上是不可移植的(上面的联合技巧依赖于类型惩罚,这是未定义的行为,但有些编译器支持。)

答案 1 :(得分:2)

没有“这个对象的成员函数”这样的东西。成员函数由同一个类的所有对象有效地共享 - 它只是代码而不是数据。 this通常作为额外的隐藏参数传递给这些函数。

实际上,编译器会转换它:

struct Obj
{
  int a;

  int foo(int x) const { return x * a; }
};

int bar(const Obj &o)
{ return o.foo(42); }

这样的事情:

struct Obj
{
  int a;

  static int foo(const Obj *this, int x) { return this->x * a; }
};

int bar(const Obj &o)
{ return Obj::foo(&o, 42); }

正如你所看到的,没有“&(o.foo)”这样的东西。 obj的所有实例都具有相同的foo

此外,C ++不允许您访问此“静态foo”,正是因为它是一个实现细节,编译器可以选择不同的方式(或者如果平台指示它)。你能得到的最好的是指向函数的成员指针&Obj::foo。当然,这不是一个真正的地址,它更像是类Obj中的偏移。而且你需要明确地说出它的名字 - 没有办法在不使用它的名字的情况下引用“封闭函数”。

在没有命名函数的情况下,你能得到的最好的是预定义变量__func__(在C ++ 11中引入),它包含一些特定于编译器的函数名称形式。