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: Thu, 27 Oct 2022 15:59:56 +0100
On Thu, Oct 27, 2022 at 1:45 PM Jarrad Waterloo <descender76_at_[hidden]> wrote:
>
> Your second reason is captured in the following. Please give it a look, your comments and your support.
>
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2603r1.html


If I understand your proposal correctly, then I've implemented it as
follows. The following code works properly with clang++ and g++ if you
do "-std=c++20".

#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 void Print(void) { cout << "Base class!" << endl; }
};

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

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

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

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

protected:
    Retval (*const p)(void*,Args...);
    MemFuncPtrBase( std::type_info const &arg_ti, Retval
(*arg_p)(void*,Args...) ) : ti(arg_ti), p(arg_p) {}

public:

    /* =================================================
        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);
    }
};

template<class Retval, class T, class... Args>
class MemFuncPtrT final : public MemFuncPtrBase< Retval, Args... > {
protected:

    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:

    typedef MemFuncPtrBase< Retval, Args... > Base;

    MemFuncPtrT( Retval (T:: *const arg)(Args...) )
      : Base( typeid(T), Address_Of_Member_Function(arg) ) {}

    Base &base(void) { return *this; }
};

int main(int const argc, char **const argv)
{
    MemFuncPtrT mfp1( &NumberPrinter::Print );
    MemFuncPtrT mfp2( &HexadecimalNumberPrinter::Print );

    static_assert( std::is_same_v< decltype(mfp1)::Base, decltype(mfp2)::Base >,
        "These two types should be the same");

    using MemFuncPtr = decltype(mfp1)::Base;

    // On the next line, we decide at runtime which method to call
    MemFuncPtr mfp( (argc % 2) ? mfp1.base() : mfp2.base() );

    HexadecimalNumberPrinter obj;

    mfp(&obj)();

    cout << mfp.ti.name() << endl;
}

Received on 2022-10-27 15:00:09