C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Allow conversion of memfunc pointers to func pointers

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Sun, 14 Jan 2024 23:27:56 +0000
On Sat, Jan 13, 2024 at 7:04 PM Jarrad Waterloo wrote:
>
> I really think this needs to be a language feature that is portable.


I agree that it should be a portable language feature in C++26, but
for the time being I'm having fun trying to implement it on different
compilers.
As I mentioned in a post a few days ago, I already got it working on GNU g++:

    https://godbolt.org/z/TvK5Y7aWh

but that was made easy by the GNU compiler extension that allows you
to get the memory address of a virtual function (see here:
https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html )

It's easy to get the address of any class's vtable if you have an
object of the class, for example with the following function:

    template<typename T>
    void (*GetVtable(T const &arg))(void)
    {
        static T obj;
        return *static_cast<void (*const *)(void)>(static_cast<void
const *>(&arg));
    }

So then when you get a pointer to a member function, you can extract
the vtable offset and add it to the address of the vtable -- but what
if T doesn't have a default constructor? Or what if T is an abstract
class? Or what if T doesn't have any accessible constructors? The
above function will fail to compile if it can't create an object of
type T.

But I have another idea. The mangled linker symbol for the vtable for
any class is: _ZTV5Name

So if the class is called 'Donkey', then the vtable linker symbol is
'_ZTV5Donkey'.

So I rewrote the 'NonVirtual' macro to use the vtable's linker symbol:

    #define NonVirtual(Class,Member)
                \
        ([]() -> decltype(&Class::Member)
                \
        {
              \
            auto pmf = &Class::Member;
              \
            extern void (*const _ZTV5##Class)(void);
              \
            void (*const *const &vtable)(void) = &_ZTV5##Class;
              \
            auto &n =
*static_cast<std::uintptr_t*>(static_cast<void*>(&pmf)); \
            if ( 0u == (n & 1u) ) return pmf; /* no need to
devirtualise */ \
            void (*const pf)(void) = vtable[1u + n];
              \
            std::memcpy(&pmf,&pf,sizeof pf);
              \
            return pmf;
              \
        }())

And then I tried it out with LLVM clang++ and also the Intel compiler:

    https://godbolt.org/z/WdT5MMcG3

And while I'm talking . . . maybe it would be worth mentioning that
name mangling should be standardised too?

Received on 2024-01-14 23:28:04