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, 7 Jan 2024 20:19:17 +0000
On Sat, Jan 6, 2024 at 12:14 PM Frederick Virchanza Gotham wrote:
>
> And therefore, even on implementations that use thunks,
> "std::memfunc_to_func" should not return the address of the thunk, but
> rather it should return the address of the bonafide function. Then
> it's the job of "std::invoke_func_as_memfunc" to adjust the 'this'
> pointer where necessary.


I'm at the limit of my intelligence here trying to understand how
pointers-to-member-functions work. For the timebeing I'm focusing on
the g++ implementation that looks like this:

    struct PointerToMemberFunction {
       union {
            void (*funcadr)(void); // always even
            std::uintptr_t offset_into_vtable; // always odd
       };
       std::uintptr_t delta_this;
    };

And I've written a function that describes a
pointer-to-member-function on g++ and clang++:

    #include <string>
    template<typename R, typename Class, typename... Params>
    std::string DescribeMemberFunc( R (Class::*p)(Params...) )
    {
        using std::uintptr_t;
        static_assert( sizeof(void(*)(void)) == sizeof(void*) );
        static_assert( sizeof(void(*)(void)) == sizeof(uintptr_t) );
        static_assert( sizeof(p) == 2u*sizeof(uintptr_t) );

        auto &n = *static_cast<uintptr_t*>(static_cast<void*>(&p));

        std::string s("PMF is ");

        if ( n & 1u )
        {
            // n is an offset into a vtable
            --n;
            s += "vtable offset " + std::to_string(n /
sizeof(void(*)(void))) + " with 'this' + ";
        }
        else
        {
            // n is just a pointer to a function
            s += "func pointer with 'this' + ";
        }

        auto this_offset = (&n)[1u];

        s += std::to_string(this_offset);
        s += '.';
        return s;
    }

And I've written some test code:

    https://godbolt.org/z/sEEn9n64h

In my example code, the address of a HouseBoat is a few bytes less
than the address of a Residence, and therefore I'd expect to see some
adjustment of the 'this' pointer -- but surprisingly the
pointer-to-member-function has the 'delta_this' set to 0. It seems
that the 'this' pointer gets adjusted when you actually invoke the
pointer-to-member-function on an object (and it seems that the 'delta
this' is gotten from the type of the pointer-to-member function).

What I don't understand though . . . . is if the 'delta this' can be
gotten from the type of the pointer-to-member-function, then why are
pointers to member functions 128-Bit with a 64 bits for the 'delta
this' if it's always going to be zero?

But anyway . . . if we are to retain the class type when calling
"memfunc_to_func", then we'll have to put the class type in as the
first parameter, something like:

    int (MyClass::*pmf)(double) = &MyClass::SomeMethod;

    int (*pf)(MyClass*,double) = std::memfunc_to_func(p);

But "pf" is not intended to be used as a normal function. Instead you must do:

    std::invoke_func_as_memfunc(pf, &myvar, 56.7L);

If we don't put that extra parameter in there, then instead we would
need to tell 'invoke_func_as_memfunc' the class type, perhaps
something like:

        int (MyClass::*pmf)(double) = &MyClass::SomeMethod;
        int (*pf)(double) = std::memfunc_to_func(p);
        std::invoke_func_as_memfunc<MyClass>(pf, &myvar, 56.7L); //
note the template parameter here

Received on 2024-01-07 20:19:24