To anyone coming accross that issue in 2022, I will offer a new approach that is compliant with the initial request.
I will welcome any feedback about this solution, and hope this can help anyone coming accross that topic.
First we need to understand that the core issue -as some highlighted already- is that a non-static method (which can access object's members etc..) need to access 'this', the object's instance pointer. However since we want our function to be a callback, we cannot modify the way it will be called. This is why we need to have a function that can access its object's 'this' pointer.
My solution is to modify a dummy function clone's code at runtime and pass its address as the callback function, which then once called will be able to resolve its assigned object pointer. This dummy is templated in a wrapper, so it can adapt to any desired signature.
First here are my repos links in case the code is updated in future (https://github.com/Ezarkei/BindFunctorToC https://gitlab.com/Ezarkei/BindFunctorToC)
So here is how to use it:
Object instance{}; //Create an instance
BindFunctorToC<Object> binder{instance}; //Create a binder on that instance
void(*fPtr)(void){binder()}; //Get the C-style function pointer from the binder, here the signature is void(*)(void)
fPtr(); //Call the C-style function pointer
Then a more detailed example:
#include "BindFunctorToC.hpp"
#include <iostream>
struct Foo {
int operator()(std::string const &other) const noexcept { //This is our functor, the "entry point" to our object from the C-style function pointer call
return Bar(other); //Here this functor simply forwards to a method
}
int Bar(std::string const &other) const noexcept { //This method is non-static and will use an object's member: _str
std::cout << _str << ' ' << other << std::endl; //Beeing able to access _str here clearly shows that it's not a trick, we have a direct access to 'this'
return 0;
}
std::string const _str{"default"};
};
static void CallBack(int(*callback)(std::string const &)) noexcept { //This is the kind of use case we want to be able to accomplish, a simple C-style function pointer is passed as parameter but it will effectively call a non-static method on an object
callback("world"); //Here we will call foo1 instance's operator(), hence foo1's 'Bar' method
}
int main(void) {
Foo foo1{"hello"}, foo2{"foo"}; //First we declare 2 instances of Foo, with 2 different member values so we can distinguish them well
BindFunctorToC<Foo> binder1{foo1}, binder2{foo2}; //For every instance a binder is needed
int(*ptr)(std::string const &){binder1()}; //We then construct a C-style function pointer with Foo's operator() signature and initialize it to binder1 function by calling binder1's operator()
CallBack(ptr); //Here we will pass our C-style function pointer to the C api which may need it as a callback
return binder2()("bar"); //Proof that we work on instances, first the operator() will get the C-style function pointer, then we call it and return its value to show the signatures deduction works
}
And finally the binder code available on the repos (content of BindFunctorToC.hpp):
//******************************************************************************
//* Copyright (c) 2022 Ezarkei *
//* *
//* This document is under the MIT License *
//******************************************************************************
#ifndef BINDFUNCTORTOC_HPP_
#define BINDFUNCTORTOC_HPP_
#if ((defined(__i386__) || defined(__x86_64__) || defined(__arm__)) && (defined(__linux__) || defined(__linux) || defined(linux) || defined(__unix__) || defined(__unix))) || (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
#if defined(_DEBUG) || defined(DEBUG)
#error Requires release compilation (windows)
#endif
#define __win32__
#endif
#ifdef __win32__
#define __attribute__(__)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#include <cstring>
#endif
#include <type_traits>
#include <stdexcept>
#include <string>
#ifdef __win32__
#define __DCL__(_) ((typename decltype(_))(_))
#else
#define __DCL__(_) (_)
#endif
#define __FLG__ 0x21626e636967616d
template<typename R> struct __TTRf__ {
explicit __TTRf__(void) noexcept = delete;
using _R = R &;
};
template<typename> struct __BndFcntrTC__;
template<typename R, typename T, typename ...A> struct __BndFcntrTC__<R(T::*)(A...)> {
public:
explicit __BndFcntrTC__(T &);
~__BndFcntrTC__(void) noexcept;
R(*operator()(void) const noexcept)(A...);
R(&_mppr)(__BndFcntrTC__<R(T::*)(A...)> &, typename __TTRf__<A>::_R...) noexcept = &__MdmMppr__<>;
private:
void __MplcDdrss__(void const *const);
template<typename O = R> static typename std::enable_if<std::is_same<O, void>::value, void>::type __MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &, typename __TTRf__<A>::_R...) noexcept;
template<typename O = R> static typename std::enable_if<!std::is_same<O, void>::value, O>::type __MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &, typename __TTRf__<A>::_R...) noexcept;
static std::size_t __PgSzClcltr__(void) noexcept;
static std::size_t __RwTmpltSzClcltr__(void) noexcept;
static std::size_t const _flg, _pgSz, _rwTmpltSz, _sgmntSz;
T &_trgt;
void *_sgmnt;
};
template<typename> struct __CnstNxcptBstrct__;
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...)> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = R(T::*)(A...);
};
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...) const> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = typename __CnstNxcptBstrct__<R(T::*)(A...)>::_S;
};
#if __cplusplus > 201402L
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...) noexcept> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = typename __CnstNxcptBstrct__<R(T::*)(A...)>::_S;
};
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...) const noexcept> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = typename __CnstNxcptBstrct__<R(T::*)(A...)>::_S;
};
#endif
template<typename T> class BindFunctorToC : public __BndFcntrTC__<typename __CnstNxcptBstrct__<decltype(&T::operator())>::_S> {
public:
explicit BindFunctorToC(T &);
};
template<typename R, typename T, typename ...A> __attribute__((noinline, unused)) void __SzClcltrE__(void) noexcept;
template<typename R, typename T, typename ...A> __attribute__((noinline, optimize(3))) typename std::enable_if<std::is_same<R, void>::value, void>::type __RwTmplt__(A...) noexcept;
template<typename R, typename T, typename ...A> __attribute__((noinline, optimize(3))) typename std::enable_if<!std::is_same<R, void>::value, R>::type __RwTmplt__(A...) noexcept;
template<typename R, typename T, typename ...A> __BndFcntrTC__<R(T::*)(A...)>::__BndFcntrTC__(T &trgt) : _trgt{trgt} {
#ifdef __win32__
(void const *const)(_rwTmpltSz + _pgSz);
_sgmnt = VirtualAlloc(NULL, _sgmntSz, MEM_COMMIT, PAGE_READWRITE);
if (!_sgmnt)
throw std::runtime_error{std::string{"VirtualAlloc error :: "} + std::to_string(GetLastError())};
#else
_sgmnt = mmap(nullptr, _sgmntSz, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (MAP_FAILED == _sgmnt)
throw std::runtime_error{std::string{"Mmap error :: "} + strerror(errno)};
#endif
void const *const sgmnt{(void const *)__DCL__((&__RwTmplt__<R, T, A...>))};
std::memcpy(_sgmnt, sgmnt, _rwTmpltSz);
__MplcDdrss__(this);
#ifdef __win32__
unsigned long dscrd;
if (!VirtualProtect(_sgmnt, _sgmntSz, PAGE_EXECUTE_READ, &dscrd))
throw std::runtime_error{std::string{"VirtualProtect error :: "} + std::to_string(GetLastError())};
#else
if (mprotect(_sgmnt, _sgmntSz, PROT_EXEC | PROT_READ))
throw std::runtime_error{std::string{"Mprotect error :: "} + strerror(errno)};
__builtin___clear_cache(_sgmnt, (uint8_t*)_sgmnt + _rwTmpltSz);
#endif
}
template<typename R, typename T, typename ...A> __BndFcntrTC__<R(T::*)(A...)>::~__BndFcntrTC__(void) noexcept {
#ifdef __win32__
if (!VirtualFree(_sgmnt, 0, MEM_RELEASE))
#else
if (munmap(_sgmnt, _sgmntSz))
#endif
abort();
}
template<typename R, typename T, typename ...A> R(*__BndFcntrTC__<R(T::*)(A...)>::operator()(void) const noexcept)(A...) {
return (R(*)(A...))_sgmnt;
}
template<typename R, typename T, typename ...A> void __BndFcntrTC__<R(T::*)(A...)>::__MplcDdrss__(void const *const ddrss) {
std::size_t const tht{(std::size_t const)ddrss};
uint8_t *ffst{nullptr}, m{0};
for (std::size_t i{0}, j{0}, k{0}; !ffst && _rwTmpltSz > i; ++i)
if (j[(uint8_t*)&_flg] == i[(uint8_t*)_sgmnt]) {
if (!j++)
k = i;
else if (sizeof(void *volatile const) <= j)
ffst = (uint8_t*)_sgmnt + k;
} else if (j)
j = 0;
if (ffst)
std::memcpy(ffst, &tht, sizeof(void *volatile const));
else {
for (std::size_t i{0}; !ffst && _rwTmpltSz > i; ++i)
for (uint8_t l{0}; !ffst && 8 > l; l += 4)
for (std::size_t j{0}, k{0}; _rwTmpltSz > i + j + k && 7 > j; 2 == j ? (j += 2, k = l) : ++j)
if (!(j % 4 ? j % 2 ? (uint8_t{(uint8_t)(j[(uint8_t *)_sgmnt + i + k] << 4)} >> 4) == uint8_t{(uint8_t)((j / 4 ? 3 : 1)[(uint8_t *)&_flg] << 4)} >> 4 : (uint8_t{(uint8_t)(j[(uint8_t *)_sgmnt + i + k] << 4)} >> 4) == (j / 4 ? 3 : 1)[(uint8_t *)&_flg] >> 4 : j[(uint8_t *)_sgmnt + i + k] == (j / 2)[(uint8_t *)&_flg]))
j = 7;
else if (6 == j) {
ffst = (uint8_t *)_sgmnt + i;
m = l;
}
if (ffst)
for (std::size_t i{0}, k{0}; 7 > i; 2 == i ? (i += 2, k = m) : ++i)
i % 4 ? ((i[ffst + k] >>= 4) <<= 4) |= i % 2 ? uint8_t{(uint8_t)((i / 4 ? 3 : 1)[(uint8_t *)&tht] << 4)} >> 4 : (i / 4 ? 3 : 1)[(uint8_t *)&tht] >> 4 : i[ffst + k] = (i / 2)[(uint8_t *)&tht];
}
if (!ffst)
throw std::runtime_error{"Failed to resolve flag offset"};
}
template<typename R, typename T, typename ...A> template<typename O> typename std::enable_if<std::is_same<O, void>::value, void>::type __BndFcntrTC__<R(T::*)(A...)>::__MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &tht, typename __TTRf__<A>::_R... __flds__) noexcept {
tht._trgt.operator()(std::forward<A>(__flds__)...);
}
template<typename R, typename T, typename ...A> template<typename O> typename std::enable_if<!std::is_same<O, void>::value, O>::type __BndFcntrTC__<R(T::*)(A...)>::__MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &tht, typename __TTRf__<A>::_R... __flds__) noexcept {
return tht._trgt.operator()(std::forward<A>(__flds__)...);
}
template<typename R, typename T, typename ...A> void __SzClcltrE__(void) noexcept {
__SzClcltrE__<R, T, A...>();
}
template<typename R, typename T, typename ...A> typename std::enable_if<std::is_same<R, void>::value, void>::type __RwTmplt__(A... __flds__) noexcept {
void *volatile const __RwTmpltRmPtr__{(void *)__FLG__};
__BndFcntrTC__<R(T::*)(A...)> &tht{*((__BndFcntrTC__<R(T::*)(A...)> *const)__RwTmpltRmPtr__)};
tht._mppr(tht, __flds__...);
}
template<typename R, typename T, typename ...A> typename std::enable_if<!std::is_same<R, void>::value, R>::type __RwTmplt__(A... __flds__) noexcept {
void *volatile const __RwTmpltRmPtr__{(void *)__FLG__};
__BndFcntrTC__<R(T::*)(A...)> &tht{*((__BndFcntrTC__<R(T::*)(A...)> *const)__RwTmpltRmPtr__)};
return tht._mppr(tht, __flds__...);
}
template<typename R, typename T, typename ...A> std::size_t __BndFcntrTC__<R(T::*)(A...)>::__PgSzClcltr__(void) noexcept {
#ifdef __win32__
SYSTEM_INFO nf{};
GetSystemInfo(&nf);
return nf.dwPageSize;
#else
return (std::size_t)sysconf(_SC_PAGESIZE);
#endif
}
template<typename R, typename T, typename ...A> std::size_t __BndFcntrTC__<R(T::*)(A...)>::__RwTmpltSzClcltr__(void) noexcept {
if ((std::size_t)__DCL__((&__RwTmplt__<R, T, A...>)) > (std::size_t)&__SzClcltrE__<R, T, A...>)
abort();
return (std::size_t)&__SzClcltrE__<R, T, A...> - (std::size_t)__DCL__((&__RwTmplt__<R, T, A...>));
}
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_flg{(std::size_t)__FLG__};
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_pgSz{__PgSzClcltr__()};
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_rwTmpltSz{__RwTmpltSzClcltr__()};
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_sgmntSz{(_rwTmpltSz / _pgSz + 1) * _pgSz};
template<typename T> BindFunctorToC<T>::BindFunctorToC(T &trgt) : __BndFcntrTC__<typename __CnstNxcptBstrct__<decltype(&T::operator())>::_S>(trgt) {
}
#ifdef __win32__
#undef __win32__
#undef __attribute__
#endif
#undef __DCL__
#undef __FLG__
#else
#error Unknown system ; supports unix(-like) (x86_64, i386, arm) and windows (x64, x32)
#endif
#endif