2

I searched this article: C++ : Getting function virtual 'address' with member function pointer

In order to test if the virtual member function is usually at the beginning address of the object, I wrote the code as following:

#include <pwd.h>
#include <string.h>
#include <stdio.h>

class Base
{
public:
    int mBase1;
    char mBase2;
    virtual void foo()
    {
        fprintf(stderr,"Base foo called\n");
    }
};

class Child: public Base
{
public:
    int cc;
    virtual void foo()
    {
        fprintf(stderr,"Child foo called");
    }
};


int main(int argc, char *argv[])
{
    Base bb;
    fprintf(stderr,"[mBase1 %p][mBase2 %p] [foo %p]\n",&bb.mBase1,&bb.mBase2,&Base::foo);
    return 0;
}

when compiling, I got a warning:

test.cpp:30:88: warning ‘%p’ expects argument of type ‘void*’, but argument 5 has type ‘void (Base::*)()’ [-Wformat]

The output is:

[mBase1 0xbfc2ca38][mBase2 0xbfc2ca3c] [foo 0x1]

I consider it's wired.

  1. Is there any "pretty method" to get the member function (not static member function address?). Besides the following method, is there any other elegant way?

    typedef void (Base::*Foo)();
    Foo f = &Base::foo();
    
  2. Why doesn't &Base::foo get the correct C++ member address?

Community
  • 1
  • 1
Charlie.cui
  • 63
  • 1
  • 4
  • 1
    The function itself will *not* be at the beginning of the object; a pointer to the v-table will (generally) be at the beginning of the object, and within the pointed to table you will find a pointer to the function (but not the function itself). – Matthieu M. Feb 16 '13 at 13:35
  • "Normal" pointers, including function pointers, are usually represented as machine addresses; member pointers aren't. Consider that `Derived d; (d.*f)();` *works*, but calls `Derived::Foo` as one would expect. You couldn't do that if `f` were *one* specific address. – molbdnilo Feb 16 '13 at 15:26

3 Answers3

5

Why doesn't &Base::foo get the correct C++ member address?

Pointers to member are different types than pointers (they are, in fact, more offsets than types themselves) and, in particular, they cannot be converted to void* (they can on GCC, but it's probably a compiler extension).

See Paragraph 8.3.3/3 of the C++11 Standard:

A pointer to member shall not point to** a static member of a class (9.4), a member with reference type, or “cv void.” [ Note: See also 5.3 and 5.5. The type “pointer to member” is distinct from the type “pointer”, that is, a pointer to member is declared only by the pointer to member declarator syntax, and never by the pointer declarator syntax. There is no “reference-to-member” type in C++. —end note ]

Also, Paragraph 5.2.10/10 (about reinterpret_cast<>) defines the only possible conversions for pointers to member as follows:

A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types. The null member pointer value (4.11) is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:

— converting a prvalue of type “pointer to member function” to a different pointer to member function type and back to its original type yields the original pointer to member value.

— converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer to member value.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
1

As said before pointers to members are not pointers and their conversions to types like int or void * are meaningless in general. In particular the 0x1 most likely means the first or second entry in the vtable of the object. Let's consider this code:

void (*Base::*test)() = &Base::foo
Child f;
f.*test();

For the third line the compiler gets the address of f. Converts it to the address of a Base object, which in this case is a NOOP) reads from that address the address of the vtable for f (the vtable is usually the first data item in an object), then adds two that address 1*sizeof(void*) and calls the function at that address largely something equivalent to this C :

typedef void (*FnPtr)();
FnPtr *vtable=*(FnPtr **)&(Base &)f;
(*(vtable[(int)test]))(); or (*(vtable[(int)test - 1]))();

This is only hypothesis which is likely true for the exact version of the compiler you have. No guarantee of portability even to future compiler versions.

C. Szabo
  • 97
  • 7
0

Why doesn't &Base::foo get the correct C++ member address?

Base::foo is not a static method. There's no foo that belongs to Base. I wonder what the [foo 0x1] means (eg: why the 0x1 if the whole thing doesn't make much sense)?

Alien
  • 52
  • 3