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: Sat, 27 Jan 2024 00:58:58 +0000
On Fri, Jan 26, 2024 at 12:06 AM Thiago Macieira wrote:
>
> This linker change is DoA for ABI purposes.


Jarrad, I have implemented your "direct" member function pointers on
x86_64 with GNU compilers and build tools. I did so by just editing
the linker to provide a map of typeinfo's to vtables. I didn't need to
edit the compiler-proper.

I got my linker working just now. Here's the pull request comparison
against the trunk of 'binutils' which contains the GNU bfd linker:

        https://github.com/bminor/binutils-gdb/compare/8409b75...healytpk:linker-vtable:e4adc46

In the coming days I'm gonna ask Arthur and Matt if I can stick it up
on GodBolt (or at least ask them how to host my own little mini
GodBolt on my own webspace).

Using my linker, I can build the following program which prints out:

    Polymorphic Facilitator for Base = 0x562af0ed1c48
    Polymorphic Facilitator for Derived = 0x562af0ed1c30
    Hi, I'm Derived!
    Hi, I'm Base!
    Hi, I'm Derived!

Here's my program:

    struct Base {
        virtual void Speak(void) { std::cout << "Hi, I'm Base!\n"; }
    };

    struct Derived : Base {
        void Speak(void) override { std::cout << "Hi, I'm Derived!\n"; }
    };

    int main(void)
    {
        std::cout << "Polymorphic Facilitator for Base = " <<
get_polymorphic_facilitator(typeid(Base )) << std::endl;
        std::cout << "Polymorphic Facilitator for Derived = " <<
get_polymorphic_facilitator(typeid(Derived)) << std::endl;

        Derived obj;

        void (Base::*pmf1)(void) = &Base::Speak;
        (obj.*pmf1)();

        void (Base::*pmf2)(void) = std::devirtualise(pmf1,
std::get_polymorphic_facilitator(typeid(Base)));
        (obj.*pmf2)();

        void (Base::*pmf3)(void) = std::devirtualise(pmf1,
std::get_polymorphic_facilitator(typeid(Derived)));
        (obj.*pmf3)();
    }

And here's how I implemented 'std::devirtualise' and
'std::get_polymorphic_facilitator':

    #include <cassert> // assert
    #include <cstring> // memcpy
    #include <algorithm> // lower_bound
    #include <typeinfo> // type_info

    extern uint32_t __map_typeinfo_vtable[4u];

    namespace std {
        void const *get_polymorphic_facilitator(std::type_info const &ti)
        {
            using std::uint32_t, std::pair;
            uint32_t const size_in_bytes = __map_typeinfo_vtable[0u];
            if ( 0u == size_in_bytes ) return nullptr;
            uint32_t const offset = __map_typeinfo_vtable[1u];
            char const *const start_of_section =
(char*)&__map_typeinfo_vtable - offset;
            assert( (char*)&ti > (char*)start_of_section );

            uint32_t const offset_of_typeinfo = (char*)&ti -
(char*)start_of_section;

            pair<uint32_t,uint32_t> const *const p =
(pair<uint32_t,uint32_t> const *)&__map_typeinfo_vtable[2u];
            pair<uint32_t,uint32_t> const *const q = p +
(size_in_bytes / (4u + 4u));

            pair<uint32_t,uint32_t> const *const found =
              std::lower_bound(
                p,
                q,
                offset_of_typeinfo,
                [](std::pair<uint32_t,uint32_t> const &a, uint32_t
const n){ return a.first < n; });

            if ( q == found ) return nullptr;

            return (char*)start_of_section + found->second + 0x10;
        }

        template<typename R, class Class, typename... Params>
        R (Class::*devirtualise(R (Class::*pmf)(Params...), void const
*const p) noexcept)(Params... args)
        {
            auto n = *static_cast<std::uintptr_t*>(static_cast<void*>(&pmf));
            if ( 0u == (n-- & 1u) ) return pmf; /* no need to devirtualise */
            void const *const *pvtable = static_cast<void const *const*>(p);
            std::memcpy(&pmf, pvtable + n, sizeof(void(*)(void)));
            return pmf;
        }
    }

Received on 2024-01-27 00:59:10