clambake
clambake

Reputation: 822

Getting address to a member function inside the member function

I would like to get the address of a member function inside the same member function. The this pointer is acquired already so I know which object I am called from. Of course the compiler complains that I'm trying to use a pointer to this function while not calling it. I see the point for having this rule outside an object. Is there no way to get "MY OWN" (the functions address when it is called) address?

This is only for debugging purposes and should be used in a macro. It would be great to have a way to do this.

Code to clarify:

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

This is on an embedded system I do not get why there is absolutely no way to get the address where do_stuff is saved in flash.

Edit I tried getting the "pointer to member function" like this:

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

It gives me 0x08013E85 which I look up in my linker map and it is exactly the function I am looking for. This is mdk-arm using Keil µVision 5 as a compiler. Is this correct?

Edit 2: All kinds of type casts give me invalid type conversion. Although printf's output is correct: Printf uses variadic macros. Let's rebuild that:

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);
    ...
}

Nasty hack, but it worked. But why does this work? Sadly I don't have the implementation of __va_arg(...). How does __va_arg typecast values to the given type? Is there a way to use that feature somehow?

Upvotes: 2

Views: 1533

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171283

A pointer-to-member function is generally twice the size of a normal pointer like a void* so there is no direct conversion possible in standard C++.

https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html documents a GCC extension which allows you to get a normal (non-member) function pointer by combining this and &CApp::Start (that page also explains why a pointer to member function has additional info beyond just the function address).

That extension could be used like so:

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

I would guess that your hack with va_arg works because your compiler represents the pointer-to-member-function &CApp::Start as a double-word object, with the function address in one of the words, which you read via the varargs hack because two words get pushed onto the stack and then you only read one of them. You might be able to get similar results using a 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

You can generalise that using a template:

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. it would be better to use uintptr_t not int32_t for portability to systems where pointers are not 32-bits, although this sort of hackery is inherently non-portable (the union trick above relies on type-punning, which is undefined behaviour but supported by some compilers).

Upvotes: 3

There is no such thing as "this object's member function." The member functions are effectively shared by all objects of the same class - it's just code, not data. this is usually passed as an extra hidden parameter to these functions.

In effect, the compiler transforms this:

struct Obj
{
  int a;

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

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

into something like this:

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); }

As you can see, there's no such thing as "&(o.foo)". All instances of obj have the same foo.

In addition, C++ does not give you access to this "staticised foo", precisely because it's an implementation detail and compilers could do it differently if they so choose (or if the platform dictates it). The best you can get is the pointer-to-member to the function, &Obj::foo. Of course, this is not a real address, it's more like an offset within the class Obj. And you need to spell out its name explicitly - there's no way to refer to "the enclosing function" without using its name.

The best you can get without naming the function is the predefined variable __func__ (introduced in C++11), which contains some compiler-specific form of the function's name.

Upvotes: 2

Related Questions