4

If I have a pure virtual base class with several derivations of it...

class Base
{
public:
   virtual void method1() = 0;
}

class Derived1 : public Base
{
public:
   void method1() override { ... }
}

class Derived2 : public Base
{
public:
   void method1() override { ... }
}

Is there any way for code that holds a Base* of an object of unknown derived type to determine the address of the method1() function for the object it holds the Base* pointer to?

What I want to do is something like this:

void someOtherFunction(Base * pb)
{
   printf("If I call pb->method1(), it will call a function at %p.\n",
          &(pb->method1));
}

But I get a compiler error:

error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function.

Ideally any solution would avoid RTTI & dynamic_cast because that isn't enabled/allowed for my embedded system.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
phonetagger
  • 7,701
  • 3
  • 31
  • 55
  • 11
    XY problem? Maybe there's a better way to do what you're trying to accomplish than taking the address of the function. – Mark Ransom Jul 13 '18 at 15:29
  • I'm on my phone so I can't code it r8ght now but I think you want the curiously recuring template pattern then to can cast *this to the derived class in the base and you might get it that way. – Treebeard Jul 13 '18 at 15:38
  • 1
    [this answer](https://stackoverflow.com/a/12187582/2189130) says there's a gcc extension that might do what you want – kmdreko Jul 13 '18 at 15:38
  • 1
    Usually when you have to work that hard to subvert the compiler, you're doing something wrong. As @MarkRansom says, step back and think about what you're really trying to do. Don't swim against the current. – Caleb Jul 13 '18 at 15:40
  • agree with @MarkRansom Boost has many libraries "binding" method to some external reality ("bindig" I'm not sure all use exactly this word), without taking C style pointer – Jacek Cz Jul 13 '18 at 15:47
  • @MarkRansom - Not sure what an XY problem is; I suppose I could google it. Normally I wouldn't need to do this, but I'm trying to troubleshoot a problem that "seems like a compiler error". It's probably not, but without extra visibility into what's going on, it's hard to say for sure. I'm also getting strange behavior with GDB-through-Eclipse; when I get into this weird state, stepping on GDB just freezes, and every attempt to step results in the PC staying where it is. – phonetagger Jul 13 '18 at 15:52
  • Solution for X problem: virtual ... infoAboutDerivedClass() = 0; // =) – wtom Jul 13 '18 at 15:57
  • 1
    Possible duplicate of [Print address of virtual member function](https://stackoverflow.com/questions/3068144/print-address-of-virtual-member-function) – wtom Jul 13 '18 at 16:05
  • I am not aware of restrictions of embedded systems. But how are you initializing pb here? Base is abstract so you have to create derived object. How are you calling someOtherFunction()? What are you passing pb as? On my system, your code did not give any error. **If I call pb->method1(), it will call a function at 000000000023fdf0** –  Jul 13 '18 at 16:10
  • 3
    [XY problem](https://meta.stackexchange.com/q/66377/130940) means asking a question about a problem that's tangential to the problem you're trying to solve. You're trying to make one approach work when the best answer might be to take a different approach altogether. In your case, the real problem is the trouble you're having debugging. – Mark Ransom Jul 13 '18 at 16:10
  • @MarkRansom I definitely agree that my real problem is with my debugger. I like it when I get a NULL pointer dereference and the debugger halts right at that spot and the solution is obvious. In this case the executable crashes hard (like a NULL pointer dereference), but on the debugger when it happens, the debugger gets very confused. Seems like perhaps stack stomping, but I'm really grasping at straws here. I wouldn't think GDB stepping would simply freeze when the app it's debugging stomps on its own stack. – phonetagger Jul 13 '18 at 16:17
  • @phonetagger I just found this: https://blog.mozilla.org/nfroyd/2014/02/20/finding-addresses-of-virtual-functions/ – Guillaume Racicot Jul 13 '18 at 17:17
  • You can do this in a compiler dependent way: http://www.codeproject.com/KB/cpp/FastDelegate.aspx. Basically you need create a member function pointer, and interpret it as the linked article does it. It is unfortunate that the standard doesn't give a way to do this in a standard conformant way. – geza Jul 14 '18 at 09:41
  • @geza, Thanks. I've not read the article you pointed to yet, but you are right: There ought to be a standard way of deriving the address to which the Program Counter will ultimately jump when making a virtual function call. And the result of that derivation should be a simple 32-bit pointer on 32-bit address spaces, or a 64-bit pointer on 64-bit address spaces, etc. (I'd say "on a 32-bit architecture" etc. but address space width doesn't necessarily match the ALU width.) I.e. a simple `void*` no larger than any other `void*` for each architecture. – phonetagger Jul 15 '18 at 02:34

2 Answers2

0

What you are looking for are pointer to (virtual) member function. Note that such a pointer is not an address to a function but usually an offset inside a vtable, since the actual called function depends on the real type of the object. You can not even cast this pointer to void*, the representation is implementation-defined. This also means that you cannot find the address of the actual function that will be called in a generic way.

If you really need to know the target function to call, maybe you would be better by using several separate functions and an enum ?

Anyway, if you just want to be able to call a virtual member function through a pointer, you can do something like this:

void someOtherFunction(Base* pb)
{
    using func_t = void(Base::*)(); // Type of a pointer to member function of
                                    // signature void() and object type Base
    func_t fn = &Base::method1; // Take the address of the function
    (pb->*fn)(); // Call it. Note the extra parenthesis and the operator: ->*
                 // The actual function called depends on the actual type of pb,
                 // it can be Derived1::f() or Derived2::f() in the example code you have
}
Synxis
  • 9,236
  • 2
  • 42
  • 64
  • 3
    I think this is the opposite of what the question was asking for; wouldn't it always return the address of the function defined in the base class? – Mark Ransom Jul 13 '18 at 16:13
  • Also, nowhere does this demonstrate "finding", i.e. printing or otherwise representing, the address - only calling it, but that's not what was asked. – underscore_d Jul 13 '18 at 17:06
  • @MarkRansom There is not such thing as "address of a virtual function", it is only an offset inside a vtable, since the actual function depends on the derived type. I made my answer clearer on that. – Synxis Jul 13 '18 at 22:03
  • @underscore_d I think the question was not about printing an address but rather having a pointer to call the virtual member or finding the address of derived implementation (which, while probably feasible, will surely necessitate some compiler-specific magic) – Synxis Jul 13 '18 at 22:07
  • @Synxis that was my point, you're taking the address of a specific class function with the `Base::` syntax and bypassing the virtualness of it. – Mark Ransom Jul 14 '18 at 02:35
  • @MarkRansom What ? It is not bypassing the virtualness at all... See the question I linked. – Synxis Jul 14 '18 at 09:12
  • @MarkRansom and Synxis:I found my problem; like many problems it was really stupid. I was calling virtual member functions on an object that hadn't been fully constructed yet. I was trying to do their dependency injection between each other (manually) as part of the member initialization list of the owning class. That didn't work out real well because the constructor of one of the classes was doing its own initialization based on the results of method calls on the other, whose vtable pointer apparently hadn't been initialized yet, leading the program counter off into the weeds. – phonetagger Jul 15 '18 at 02:25
  • ...But yes, I really was wanting to find the address of the method that the program counter would ultimately jump to when doing the virtual method call. It seems like there ought to be a legal, standard way of finding that in C++. Not that I need to know now, I'm just sayin'. Like with a normal non-virtual method. It ought to be derivable in a standard legal way. And the end result should be a simple 32-bit pointer on 32-bit address spaces and a 64-bit pointer on 64-bit address spaces,etc. (I'd say "on a 32-bit architecture" etc. but address space width doesn't necessarily match the ALU width) – phonetagger Jul 15 '18 at 02:30
  • @phonetagger For stand-alone functions you can have their real address. For member functions (virtual or not) there is *no* standard way. Notably, a pointer to member is not necessarily 4 or 8 bytes long; for example, with MSVC such a pointer can go up to 20 bytes long ! – Synxis Jul 15 '18 at 22:30
  • @Synxis I've seen that before ("...up to 20 bytes long"), and I'm curious as to how that could be. Ultimately though, the Program Counter register is only 32- or 64-bits long, and however long the logical "pointer" is, by the time it gets loaded into the Program Counter for the jump instruction, it has to be just 32 or 64 bits. The method/function/routine that it jumps to has to reside at a virtual address that's identified by just 32 or 64 bits. I'm just saying there ought to be a standard way that we can derive that address, if for no other reason than just debugging or sanity-checking. – phonetagger Jul 16 '18 at 13:31
  • 2
    @phonetagger There is no standard way to find this address. Although it has to be converted to something the CPU can jump to, some implementations compute this address at the last time possible, and [things like multiple inheritance or virtual inheritance render everything complicated](https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible). – Synxis Jul 16 '18 at 23:23
-1

Without knowing what you're trying to accomplish, it's quite hard giving an advice. I'd go with the following: use lambdas.

For example, capturing and non-capturing:

Base* b = ...;

// non capturing, convertible to function pointer
auto lambda1 = [](Base*) { b->method1() }
auto fptr = static_cast<void(*)(Base*)>(lambda1);

lambda1(b);
fptr(b);

// capturing
auto lambda2 = [b]{ b->method1() };

lambda2();

// can be stored in a std::function if you need them in a collection
std::function func = lambda2;
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • How does this answer the question of how to "find", i.e. print, which implementation of the method would be called, without calling it? This is just a massively contrived way to call the method again. – underscore_d Jul 13 '18 at 17:08
  • @underscore_d It's hard to infer what OP is trying to accomplish given no information on why he has this problem. I thought I was because he was trying to store the function pointer to call it later. – Guillaume Racicot Jul 13 '18 at 17:18
  • @GuillaumeRacicot My guess was he wants a `set` (or whatever else) of those pointers. – Zereges Jul 14 '18 at 09:20
  • @Zereges yes this is what I assumed. A `std::set` or a `std::vector` of function could be replaced with those container + lambdas. This is also why I emphasized on the conversion of a lambda to a function pointer. – Guillaume Racicot Jul 14 '18 at 14:07