C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Expose architecture at compile-time (and more at runtime)

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Fri, 28 Oct 2022 10:21:28 +0100
On Fri, Oct 28, 2022 at 1:50 AM Chris Ryan via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> //[proposed example. Does not currently work] :
> void(Base::*pDecay)() = +&Base::Foo; // decay to static linkage address using '+'
> void(Base::*pDefault)() = &Base::Foo; // use default linkage


On g++ and clang++, the variable "pDefault" in memory will be a simple
integer like "1" or "2" or "3" to indicate an offset into a vtable,
and this simple integer will be shifted three places to the left and
then bitwise-OR'ed with 1, as follows:

        void (Base:: *pDefault)(void) ;

        __uint128_t n = ( index_into_vtable << 3u ) & 1u;

        std::memcpy( &pDefault, &n, sizeof pDefault );

That's how 'pDefault' works, at least on g++ and clang++. But 'pDecay'
will be different. In order for 'pDecay' to work properly, it will
need to extraneously contain either:
(a) A pointer to the vtable containing the pointer to the relevant
member function, along with an integer index into the vtable
(b) A pointer to the member function

So this will make "pDefault" and "pDecay" binary incompatible. Is my
understanding right here?


Marcin Jaczewski posted:
>What exactly is the point for that? beside this is already possible by trival template:
> <snip code snippet>

Please give a compileable example of how you would use that code,
because right now I don't understand how it works.

By the way I've realised that my previous code was way too
complicated, I can greatly simplify it as follows, there's actually no
need for a base class:

#include <cstring> // memcpy
#include <type_traits> // is_same_v - just for debugging
#include <typeinfo> // type_info
#include <iostream> // iostream
#include <ios> // dec, hex

/* =================== First let's have some sample classes */
using std::cout;
using std::endl;

struct NumberPrinter {
    long unsigned i = 0u;
    virtual int Print(int) { cout << "Base class!" << endl; return 5; }
};

struct DecimalNumberPrinter : NumberPrinter {
    int Print(int) { cout << std::dec << i++ << endl; return 5; }
};

struct HexadecimalNumberPrinter : NumberPrinter {
    int Print(int) { cout << "0x" << std::hex << i++ << endl; return 5; }
};

/* =================== Now let's have the code for MemFuncPtr */

template<class Retval, class... Args>
class MemFuncPtr {
public:
    std::type_info const &ti;

protected:

    Retval (*const p)(void*,Args...);

    template<class T>
    static Retval (*Address_Of_Member_Function( Retval (T:: *const
mp)(Args...) ))(void*, Args...)
    {
        static T obj; // Let's hope it has a default constructor

        std::uintptr_t n;
        std::memcpy(&n, &mp, sizeof n);

        Retval (*const *const v_table)(void*,Args...) =
            *static_cast<Retval(*const**)(void*,Args...)>(static_cast<void*>(&obj));

        return v_table[n >> 3u]; // This works on 'g++' and 'clang++'
    }

public:

    template<class T>
    MemFuncPtr( Retval (T:: *const arg)(Args...) )
      : ti(typeid(T)), p(Address_Of_Member_Function(arg)) {}

    /* =================================================
        Beginning of class within a class */
    class Invoker final {
        void *const p_obj;
        Retval (*const p)(void*, Args...);
    public:
        Invoker(void *const arg_p_obj, Retval (*const arg_p)(void*, Args...))
          : p_obj(arg_p_obj), p(arg_p) {}

        Retval operator()(Args... args)
        {
            return p(p_obj,args...);
        }
    };
    /* End of class within a class
    ================================================= */

    Invoker operator()(void *const arg_p)
    {
        return Invoker(arg_p, this->p);
    }
};

int main(int const argc, char **const argv)
{
    // Unfortunately the next line work
    MemFuncPtr mfp( (argc % 2) ? MemFuncPtr(&NumberPrinter::Print) :
MemFuncPtr(&HexadecimalNumberPrinter::Print) );

    HexadecimalNumberPrinter obj;

    int const retval = mfp(&obj)(8);

    cout << "Method belongs to class '" << mfp.ti.name() << "'\n";
}

Received on 2022-10-28 09:21:40