Skip to main content
Code Review

Return to Question

Update wording, add tag
Source Link

I've started to write ana header-only implementation of callback for member functions :

Do you think removing them worthsis worth it, or should I consider that most compilercompilers are c++17 compatible ? (Note that I would like to target embededan embedded system... but from what I see, these compilers are mostly based on GCC ouor Clang...)

I've started to write an header-only implementation of callback for member functions :

Do you think removing them worths it, or should I consider that most compiler are c++17 compatible ? (Note that I would like to target embeded system... but from what I see, these compilers are mostly based on GCC ou Clang...)

I've started to write a header-only implementation of callback for member functions :

Do you think removing them is worth it, or should I consider that most compilers are c++17 compatible ? (Note that I would like to target an embedded system... but from what I see, these compilers are mostly based on GCC or Clang...)

Source Link
hl037_
  • 141
  • 2

Member (virtual or not) function to C callback by generating static version at compile time

I've started to write an header-only implementation of callback for member functions :


#include <cstddef>
#include <utility>
namespace _callback_internal{
/** \internal
 * A Type list to store variable argument type
 */
template<typename T, typename... Args>
struct TypeRecList{
 template<size_t n, bool dummy = true> // dummy is needed for the partial specialization
 struct get{
 using target = typename TypeRecList<Args...>::template get<n - 1>;
 using type = typename target::type;
 using tail = typename target::tail;
 };
 
 template<bool dummy>
 struct get<0, dummy>{
 using target = get<0>;
 using type = T;
 using tail = TypeRecList<Args...>;
 };
};
/**
 * Canonical definition of TypedCallbackObject, so that the compiler does not complain about unrelated type
 * when accessed with ::CallbackObject<method>
 */
template <typename C, typename R, typename... Args>
struct TypedCallbackObject{
 C * this_;
 R (*callback)(C *, Args...);
 
 template <typename _C>
 inline constexpr operator TypedCallbackObject<_C, R, Args...>() {
 return TypedCallbackObject<_C, R, Args...>{reinterpret_cast<TypedCallbackObject<_C, R, Args...>>(*this)};
 }
 
 template <typename _C>
 inline constexpr TypedCallbackObject<_C, R, Args...> & castClass() {
 return *reinterpret_cast<TypedCallbackObject<_C, R, Args...>*>(this);
 }
};
/**
 * Same as TypedCallbackObject, but with a void `this_`
 */ 
template <typename R, typename... Args>
using CallbackObject = TypedCallbackObject<void, R, Args...>;
template<typename R, typename C, typename... Args>
struct CallbackDetails;
/**
 * The callback class, holding addresses of the static version of the members generated at compile time.
 */
template<auto f, typename R, typename C, typename... Args>
struct Callback{
 using Self = Callback<f, R, C, Args...>;
 /**
 * Canonical c function version of the member, taking void as first argument. mfunc permits to reinterpret cast to a function taking any type as first arg.
 */
 static R cfunc(void * _this, Args... args){
 return (static_cast<C *>(_this)->*f)(args...);
 }
 /**
 * Allow to change the first argument from a void pointer to anything.
 */
 template<typename T=void>
 static constexpr auto mfunc(){
 return reinterpret_cast<R (*)(T *, Args...)>(Self::cfunc);
 }
 using details_t = CallbackDetails<R, C, Args...>;
 using return_t = typename details_t::return_t;
 using class_t = typename details_t::class_t;
 using args_list_t = TypeRecList<Args...>;
 template <typename T=void>
 using CallbackObject = typename details_t::template CallbackObject<T>;
 
 template<typename T=void>
 static inline constexpr CallbackObject<T> callback(C * this_){
 return CallbackObject<T>{
 this_,
 Self::template mfunc<T>()
 };
 }
};
/**
 * Detail class the extract the information about the callback type.
 */
template<typename R, typename C, typename... Args>
struct CallbackDetails{
 using return_t = R;
 using class_t = C;
 using args_list_t = TypeRecList<Args...>;
 using cfunc_t = R(C::*)(Args...);
 template<auto f>
 using Callback = Callback<f, R, C, Args...>;
 template<typename T=void>
 using CallbackObject = TypedCallbackObject<T, R, Args...>;
};
/** \internal
 * Utility function to be used with `decltype()` to extract the details.
 */
template<typename R, typename C, typename... Args>
CallbackDetails<R, C, Args...> get_details(R (C::*) (Args...));
}
/**
 * Callback object class can use to group the function pointer and the first argument (bind-like)
 */
template <typename C, typename R, typename... Args>
using TypedCallbackObject = _callback_internal::TypedCallbackObject<C, R, Args...>;
/**
 * Same as TypedCallbackObject with void * as first function argument.
 */
template <typename R, typename... Args>
using CallbackObject = _callback_internal::CallbackObject<R, Args...>;
/**
 * Return the internal Callback object providing all informations abour the method.
 */
template <auto method>
using CallbackTraits = typename decltype(_callback_internal::get_details(method))::template Callback<method>;
/**
 * Construct a c function pointer that will have the same signature as \p method, but with a `void *` argument (or \p T if provided), wich should be an instance of the class method is bound to.
 */
template <auto method, typename T=void>
inline constexpr auto toCallback(){ return CallbackTraits<method>::template mfunc<T>(); }
/**
 * Construct a callback object wich binds an instance to a c function pointer that will call method from the instance.
 */
template <auto method, typename T=void>
inline constexpr auto toCallbackObject(typename CallbackTraits<method>::class_t * instance){ return CallbackTraits<method>::template callback<T>(instance); }

Here a main.cpp to test it :

#include "./callback.h"
#include <iostream>
#include <sstream>
#include <string>
void f(void * instance, std::string (*g)(void *, float, int)){
 std::cout<<"g addr : <"<<(void *)g<<"> result:"<<g(instance, 1.5, 9)<<std::endl;
}
struct A{
 int coef;
 public:
 A(int coef):coef{coef} {}
 std::string add(float f, int i){
 std::ostringstream oss;
 oss << "Add function : " << coef << ×ばつ("<< f << "+" << i << ") ) " << coef*(f+i);
 return oss.str();
 }
 
 std::string mult(float f, int i){
 std::ostringstream oss;
 oss << "Add function : " << coef << ×ばつ("<< f << "*" << i << ") ) " << coef*(f*i);
 return oss.str();
 }
};
int main(int argc, char * argv[]){
 
 A a1{1};
 A a2{2};
 f(&a1, toCallback<&A::add>());
 f(&a2, toCallback<&A::add>());
 f(&a1, toCallback<&A::mult>());
 f(&a2, toCallback<&A::mult>());
 CallbackObject<std::string, float, int> oba1 = toCallbackObject<&A::add>(&a1);
 f(oba1.this_, oba1.callback);
 CallbackObject<std::string, float, int> oba2 = toCallbackObject<&A::add>(&a2);
 f(oba2.this_, oba2.callback);
 
 TypedCallbackObject<A, std::string, float, int> oba2_ = toCallbackObject<&A::add, A>(&a2);
 f(oba2_.castClass<void>().this_, oba2_.castClass<void>().callback);
 return 0;
}

I tried to use the least of C++17 features possible, but I don't see how I could get rid of the auto in the templates...

Do you think removing them worths it, or should I consider that most compiler are c++17 compatible ? (Note that I would like to target embeded system... but from what I see, these compilers are mostly based on GCC ou Clang...)

Review appreciated too :)

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /