C++: Pointer to Monomorphic Version of Virtual Member Function

C++: Pointer to monomorphic version of virtual member function?

It's possible in GCC, but the way it's documented in C++ language extensions section suggests there's no portable way to do it.

You can do two things:

  1. If you control the class, create a non-virtual function and a virtual wrapper for it and when you know you don't need virtual dispatch, just take address of the non-virtual one.
  2. If you don't, create a template functor that will hold the member pointer and do the explicit scope call.

Pointer to a base virtual member function

You might define a non-virtual function real_g called from g so code

struct Base
{
void real_g() const {
std::cout << "Base" << std::endl;
}
virtual void g() const { real_g(); };
};

Then later in main

std::mem_fn(&Base::real_g)(d);

See wikipage on virtual method table, this C++ reference and the C++ standard n3337 or better. Read also a good C++ programming book and the documentation of your C++ compiler, e.g. GCC

See also this answer (explaining naively what are vtables, in simple cases)

Calling base class definition of virtual member function with function pointer

When you call a virtual method via a reference or a pointer you will always activate the virtual call mechanism that finds the most derived type.

Your best bet is to add an alternative function that is not virtual.

How does llvm know whether a member function pointer pointed to a virtual function?

In the documentation you linked it says:

For a virtual function, it is 1 plus the virtual table offset (in
bytes) of the function

Under Virtual Table Layout, it says:

offsets within the virtual table are determined by that allocation sequence and the natural ABI size and alignment

The offset must respect the alignment requirements of the function pointer.

The alignment requirements of a function pointer which is a "POD" type are specified in the corresponding C ABI. I assume that pointers are aligned to their size, so the address (and thus the offset) of a pointer must be an even number, and its least significant bit must be zero.

So the implementation can just check the LSB of the offset/pointer field and know that if and only if the LSB is one it is dealing with a virtual method.

Once it has the offset in the virtual table, it reads the virtual table pointer from the object and loads the function's actual address from the virtual table using the offset from the member pointer.

class C {
virtual int someMethod();
};

int invokeAMethod(C *c, int (C::*method)()) {
return (c->*method)();
}

On x86_64 clang indeed creates a check for the LSB of the "ptr" member of the method pointer:

invokeAMethod(C*, int (C::*)()): # @invokeAMethod(C*, int (C::*)())
// c is in rdi, method.adj is in rdx, and method.ptr is in rdx
// adjust this pointer
add rdi, rdx
// check whether method is virtual
test sil, 1
// if it is not, skip the following
je .LBB0_2
// load the vtable pointer from the object
mov rax, qword ptr [rdi]
// index into the vtable with the corrected offset to load actual method address
mov rsi, qword ptr [rax + rsi - 1]
.LBB0_2:
// here the actual address of the method is in rsi, we call it
// in this particular case we return the same type
// and do not need to call any destructors
// so we can tail call
jmp rsi # TAILCALL

I could not share the godbolt link for this particular example because one of my browser plugins interfered, but you can play with similar examples yourself on https://gcc.godbolt.org.

Pointer to base class sub-object. Which version of virtual function is invoked?

It seems that you are missing what dynamic binding actually means. It means exactly that even if the pointer (statically) refers to the base sub object the call will be dispatched to the (dynamic) type of the complete object.

The common implementation is by means of a virtual function table. The base sub object will store as a hidden member a pointer to the virtual function table of the actual complete type to which it belongs. All calls to virtual functions (for which dynamic dispatch is not disabled) are routed through that extra level of indirection ensuring that the final overrides will be called.

As to the specifics of how that table and the hidden pointer are managed, the compiler builds e tables for each type with virtual functions, and injects code to the different constructors to update the pointers accordingly. So while building the base subobject the pointer refers to the base vtable, but before entering the derived constructor the injected code will update the pointer (in base) to refer to the derived vtable.



Related Topics



Leave a reply



Submit