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";
}
<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