Flexible templated function class and related utilities that avoid dynamic memory allocation without limiting functionality
FuncPtr provides a flexible templated function class and related utilities while avoiding dynamic memory allocation and avoiding limited functionality.
FuncPtr provides an intuitive template interface:
FuncPtr<void(const char *)> func(puts); func("hello!\n"); // prints hello!
Several function types are supported by FuncPtr:
// Simple C functions void func(); FuncPtr<void()> fp(func); // C++ Methods struct Thing { void func(); }; Thing thing; FuncPtr<void()> fp(&thing, &Thing::func); // C functions with context void func(Thing *); FuncPtr<void()> fp(&thing, func); // Function objects struct Thing { void operator()(); }; Thing thing; FuncPtr<void()> fp(&thing);
There is no dynamic memory allocation, managing memory is placed entirely on the user. More advanced function manipulation can be accomplished with statically allocated classes:
// Function binding Binder<void(const char *), const char *> bind(putc, "hi!"); bind(); // prints hi! // Function composition Composer<int(const char *), const char *(int)> comp(puts, itoa); comp(10); // prints 10 // Function chaining Chainer<void(const char *), 2> chain; chain.attach(puts); chain.attach(puts); chain("hi!\n"); // prints hi! twice
FuncPtr allows easy support of a large range of function types in C++ APIs with very few lines of code:
class Thing { public: // The following two methods are sufficient for supporting // every supported function type void attach(FuncPtr<void()> func) { _func.attach(func); } template<typename T, typename M> void attach(T *obj, M method) { attach(FuncPtr<void()>(obj, method)); } private: FuncPtr<void()> _func; }
Additionally, FuncPtrs have several utilities for easy integration with C APIs:
// C style callbacks void register_callback(void (*callback)(void *), void *data); register_callback(&FuncPtr<void()>::thunk, &func); // C style functions without context void register_callback(void (*callback)()); Thunker thunk(func); register_callback(thunk); // mbed style callbacks void register_callback(T *obj, void (T::*M)()); register_callback(&func, &FuncPtr<void()>::call);
Revision 18:a0fde14b6c39, committed 2016-04-17
- Comitter:
- Christopher Haster
- Date:
- Sun Apr 17 23:38:04 2016 -0500
- Parent:
- 17:079c5ad807fb
- Commit message:
- Increase to 5 args
Changed in this revision
diff -r 079c5ad807fb -r a0fde14b6c39 Binder.h --- a/Binder.h Wed Apr 20 02:43:14 2016 -0500 +++ b/Binder.h Sun Apr 17 23:38:04 2016 -0500 @@ -9,11 +9,346 @@ /** Static function binding */ -template <typename F, typename B0=void, typename B1=void, typename B2=void, typename B3=void> +template <typename F, typename B0=void, typename B1=void, typename B2=void, typename B3=void, typename B4=void> class Binder; /** Static function binding */ +template <typename R, typename B0, typename B1, typename B2, typename B3, typename B4> +class Binder<R(B0, B1, B2, B3, B4), B0, B1, B2, B3, B4> { +public: + /** Create an unbound Binder + */ + Binder() {} + + /** Create a Binder, binding arguments to a function + */ + Binder(FuncPtr<R(B0, B1, B2, B3, B4)> func, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + attach(func, b0, b1, b2, b3, b4); + } + + /** Create a Binder, binding arguments to a method + */ + template <typename T, typename M> + Binder(T *obj, M method, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + attach(obj, method, b0, b1, b2, b3, b4); + } + + /** Bind arguments to a function + */ + void attach(FuncPtr<R(B0, B1, B2, B3, B4)> func, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + _func.attach(func); + _b0 = b0; _b1 = b1; _b2 = b2; _b3 = b3; _b4 = b4; + } + + /** Bind arguments to a method + */ + template <typename T, typename M> + void attach(T *obj, M method, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + attach(FuncPtr<R(B0, B1, B2, B3, B4)>(obj, method), b0, b1, b2, b3, b4); + } + + /** Call the bound function + */ + R call() { + return _func(_b0, _b1, _b2, _b3, _b4); + } + + /** Call the bound function + */ + R operator()() { + return call(); + } + + /** Test if function has been bound + */ + operator bool() const { + return _func; + } + + /** Static thunk for passing as C-style function + * @param func Binder to call passed as void pointer + */ + static R thunk(void *func) { + return static_cast<Binder<R(B0, B1, B2, B3, B4), B0, B1, B2, B3, B4>*>(func) + ->call(); + } + +private: + FuncPtr<R(B0, B1, B2, B3, B4)> _func; + B0 _b0; B1 _b1; B2 _b2; B3 _b3; B4 _b4; +}; + +/** Static function binding + */ +template <typename R, typename B0, typename B1, typename B2, typename B3, typename A0> +class Binder<R(B0, B1, B2, B3, A0), B0, B1, B2, B3> { +public: + /** Create an unbound Binder + */ + Binder() {} + + /** Create a Binder, binding arguments to a function + */ + Binder(FuncPtr<R(B0, B1, B2, B3, A0)> func, B0 b0, B1 b1, B2 b2, B3 b3) { + attach(func, b0, b1, b2, b3); + } + + /** Create a Binder, binding arguments to a method + */ + template <typename T, typename M> + Binder(T *obj, M method, B0 b0, B1 b1, B2 b2, B3 b3) { + attach(obj, method, b0, b1, b2, b3); + } + + /** Bind arguments to a function + */ + void attach(FuncPtr<R(B0, B1, B2, B3, A0)> func, B0 b0, B1 b1, B2 b2, B3 b3) { + _func.attach(func); + _b0 = b0; _b1 = b1; _b2 = b2; _b3 = b3; + } + + /** Bind arguments to a method + */ + template <typename T, typename M> + void attach(T *obj, M method, B0 b0, B1 b1, B2 b2, B3 b3) { + attach(FuncPtr<R(B0, B1, B2, B3, A0)>(obj, method), b0, b1, b2, b3); + } + + /** Call the bound function + */ + R call(A0 a0) { + return _func(_b0, _b1, _b2, _b3, a0); + } + + /** Call the bound function + */ + R operator()(A0 a0) { + return call(a0); + } + + /** Test if function has been bound + */ + operator bool() const { + return _func; + } + + /** Static thunk for passing as C-style function + * @param func Binder to call passed as void pointer + */ + static R thunk(void *func, A0 a0) { + return static_cast<Binder<R(B0, B1, B2, B3, A0), B0, B1, B2, B3>*>(func) + ->call(a0); + } + +private: + FuncPtr<R(B0, B1, B2, B3, A0)> _func; + B0 _b0; B1 _b1; B2 _b2; B3 _b3; +}; + +/** Static function binding + */ +template <typename R, typename B0, typename B1, typename B2, typename A0, typename A1> +class Binder<R(B0, B1, B2, A0, A1), B0, B1, B2> { +public: + /** Create an unbound Binder + */ + Binder() {} + + /** Create a Binder, binding arguments to a function + */ + Binder(FuncPtr<R(B0, B1, B2, A0, A1)> func, B0 b0, B1 b1, B2 b2) { + attach(func, b0, b1, b2); + } + + /** Create a Binder, binding arguments to a method + */ + template <typename T, typename M> + Binder(T *obj, M method, B0 b0, B1 b1, B2 b2) { + attach(obj, method, b0, b1, b2); + } + + /** Bind arguments to a function + */ + void attach(FuncPtr<R(B0, B1, B2, A0, A1)> func, B0 b0, B1 b1, B2 b2) { + _func.attach(func); + _b0 = b0; _b1 = b1; _b2 = b2; + } + + /** Bind arguments to a method + */ + template <typename T, typename M> + void attach(T *obj, M method, B0 b0, B1 b1, B2 b2) { + attach(FuncPtr<R(B0, B1, B2, A0, A1)>(obj, method), b0, b1, b2); + } + + /** Call the bound function + */ + R call(A0 a0, A1 a1) { + return _func(_b0, _b1, _b2, a0, a1); + } + + /** Call the bound function + */ + R operator()(A0 a0, A1 a1) { + return call(a0, a1); + } + + /** Test if function has been bound + */ + operator bool() const { + return _func; + } + + /** Static thunk for passing as C-style function + * @param func Binder to call passed as void pointer + */ + static R thunk(void *func, A0 a0, A1 a1) { + return static_cast<Binder<R(B0, B1, B2, A0, A1), B0, B1, B2>*>(func) + ->call(a0, a1); + } + +private: + FuncPtr<R(B0, B1, B2, A0, A1)> _func; + B0 _b0; B1 _b1; B2 _b2; +}; + +/** Static function binding + */ +template <typename R, typename B0, typename B1, typename A0, typename A1, typename A2> +class Binder<R(B0, B1, A0, A1, A2), B0, B1> { +public: + /** Create an unbound Binder + */ + Binder() {} + + /** Create a Binder, binding arguments to a function + */ + Binder(FuncPtr<R(B0, B1, A0, A1, A2)> func, B0 b0, B1 b1) { + attach(func, b0, b1); + } + + /** Create a Binder, binding arguments to a method + */ + template <typename T, typename M> + Binder(T *obj, M method, B0 b0, B1 b1) { + attach(obj, method, b0, b1); + } + + /** Bind arguments to a function + */ + void attach(FuncPtr<R(B0, B1, A0, A1, A2)> func, B0 b0, B1 b1) { + _func.attach(func); + _b0 = b0; _b1 = b1; + } + + /** Bind arguments to a method + */ + template <typename T, typename M> + void attach(T *obj, M method, B0 b0, B1 b1) { + attach(FuncPtr<R(B0, B1, A0, A1, A2)>(obj, method), b0, b1); + } + + /** Call the bound function + */ + R call(A0 a0, A1 a1, A2 a2) { + return _func(_b0, _b1, a0, a1, a2); + } + + /** Call the bound function + */ + R operator()(A0 a0, A1 a1, A2 a2) { + return call(a0, a1, a2); + } + + /** Test if function has been bound + */ + operator bool() const { + return _func; + } + + /** Static thunk for passing as C-style function + * @param func Binder to call passed as void pointer + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2) { + return static_cast<Binder<R(B0, B1, A0, A1, A2), B0, B1>*>(func) + ->call(a0, a1, a2); + } + +private: + FuncPtr<R(B0, B1, A0, A1, A2)> _func; + B0 _b0; B1 _b1; +}; + +/** Static function binding + */ +template <typename R, typename B0, typename A0, typename A1, typename A2, typename A3> +class Binder<R(B0, A0, A1, A2, A3), B0> { +public: + /** Create an unbound Binder + */ + Binder() {} + + /** Create a Binder, binding arguments to a function + */ + Binder(FuncPtr<R(B0, A0, A1, A2, A3)> func, B0 b0) { + attach(func, b0); + } + + /** Create a Binder, binding arguments to a method + */ + template <typename T, typename M> + Binder(T *obj, M method, B0 b0) { + attach(obj, method, b0); + } + + /** Bind arguments to a function + */ + void attach(FuncPtr<R(B0, A0, A1, A2, A3)> func, B0 b0) { + _func.attach(func); + _b0 = b0; + } + + /** Bind arguments to a method + */ + template <typename T, typename M> + void attach(T *obj, M method, B0 b0) { + attach(FuncPtr<R(B0, A0, A1, A2, A3)>(obj, method), b0); + } + + /** Call the bound function + */ + R call(A0 a0, A1 a1, A2 a2, A3 a3) { + return _func(_b0, a0, a1, a2, a3); + } + + /** Call the bound function + */ + R operator()(A0 a0, A1 a1, A2 a2, A3 a3) { + return call(a0, a1, a2, a3); + } + + /** Test if function has been bound + */ + operator bool() const { + return _func; + } + + /** Static thunk for passing as C-style function + * @param func Binder to call passed as void pointer + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2, A3 a3) { + return static_cast<Binder<R(B0, A0, A1, A2, A3), B0>*>(func) + ->call(a0, a1, a2, a3); + } + +private: + FuncPtr<R(B0, A0, A1, A2, A3)> _func; + B0 _b0; +}; + +/** Static function binding + */ template <typename R, typename B0, typename B1, typename B2, typename B3> class Binder<R(B0, B1, B2, B3), B0, B1, B2, B3> { public:
diff -r 079c5ad807fb -r a0fde14b6c39 Chainer.h --- a/Chainer.h Wed Apr 20 02:43:14 2016 -0500 +++ b/Chainer.h Sun Apr 17 23:38:04 2016 -0500 @@ -14,6 +14,76 @@ /** Static function composition */ +template <typename A0, typename A1, typename A2, typename A3, typename A4, int N> +class Chainer<void(A0, A1, A2, A3, A4), N> { +public: + /** Access a function in the chain + */ + FuncPtr<void(A0, A1, A2, A3, A4)> &operator[](int i) { + return _funcs[i]; + } + + /** Attach a function to the chain + */ + bool attach(FuncPtr<void(A0, A1, A2, A3, A4)> func) { + for (int i = 0; i < N; i++) { + if (!_funcs[i]) { + _funcs[i].attach(func); + return true; + } + } + + return false; + } + + /** Attach a function to the chain + */ + template <typename T, typename M> + bool attach(T *obj, M method) { + return attach(FuncPtr<void(A0, A1, A2, A3, A4)>(obj, method)); + } + + /** Call each function in the chain + */ + void call(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + for (int i = 0; i < N; i++) { + if (_funcs[i]) { + _funcs[i](a0, a1, a2, a3, a4); + } + } + } + + /** Call each function in the chain + */ + void operator()(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return call(a0, a1, a2, a3, a4); + } + + /** Test if any functions have been attached + */ + operator bool() const { + for (int i = 0; i < N; i++) { + if (_funcs[i]) { + return true; + } + } + return false; + } + + /** Static thunk for passing as C-style function + * @param func Chainer to call passed as void pointer + */ + static void thunk(void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return static_cast<Chainer<void(A0, A1, A2, A3, A4), N>*>(func) + ->call(a0, a1, a2, a3, a4); + } + +private: + FuncPtr<void(A0, A1, A2, A3, A4)> _funcs[N]; +}; + +/** Static function composition + */ template <typename A0, typename A1, typename A2, typename A3, int N> class Chainer<void(A0, A1, A2, A3), N> { public:
diff -r 079c5ad807fb -r a0fde14b6c39 Composer.h --- a/Composer.h Wed Apr 20 02:43:14 2016 -0500 +++ b/Composer.h Sun Apr 17 23:38:04 2016 -0500 @@ -14,6 +14,102 @@ /** Static function composition */ +template <typename R, typename B0, typename A0, typename A1, typename A2, typename A3, typename A4> +class Composer<R(B0), B0(A0, A1, A2, A3, A4)> { +public: + /** Create an uncomposed Composer + */ + Composer() {} + + /** Create a Composer, composing two functions + */ + Composer(FuncPtr<R(B0)> f, FuncPtr<B0(A0, A1, A2, A3, A4)> g) { + attach(f, g); + } + + /** Create a Composer, composing functions and methods + */ + template <typename FT, typename FM> + Composer(FT *fobj, FM fmethod, FuncPtr<B0(A0, A1, A2, A3, A4)> g) { + attach(fobj, fmethod, g); + } + + /** Create a Composer, composing functions and methods + */ + template <typename GT, typename GM> + Composer(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { + attach(f, gobj, gmethod); + } + + /** Create a Composer, composing functions and methods + */ + template <typename FT, typename FM, typename GT, typename GM> + Composer(FT *fobj, FM fmethod, GT *gobj, GM gmethod) { + attach(fobj, fmethod, gobj, gmethod); + } + + /** Compose two functions + */ + void attach(FuncPtr<R(B0)> f, FuncPtr<B0(A0, A1, A2, A3, A4)> g) { + _f.attach(f); + _g.attach(g); + } + + /** Compose functions and methods + */ + template <typename FT, typename FM> + void attach(FT *fobj, FM fmethod, FuncPtr<B0(A0, A1, A2, A3, A4)> g) { + attach(FuncPtr<R(B0)>(fobj, fmethod), g); + } + + /** Compose functions and methods + */ + template <typename GT, typename GM> + void attach(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { + attach(f, FuncPtr<B0(A0, A1, A2, A3, A4)>(gobj, gmethod)); + } + + /** Compose functions and methods + */ + template <typename FT, typename FM, typename GT, typename GM> + void attach(FT *fobj, FM fmethod, GT *gobj, GM gmethod) { + attach(FuncPtr<R(B0)>(fobj, fmethod), + FuncPtr<B0(A0, A1, A2, A3, A4)>(gobj, gmethod)); + } + + /** Call the composed functions + */ + R call(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return _f(_g(a0, a1, a2, a3, a4)); + } + + /** Call the composed functions + */ + R operator()(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return call(a0, a1, a2, a3, a4); + } + + /** Test if functions have been composed + */ + operator bool() const { + return _f && _g; + } + + /** Static thunk for passing as C-style function + * @param func Composer to call passed as void pointer + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return static_cast<Composer<R(B0), B0(A0, A1, A2, A3, A4)>*>(func) + ->call(a0, a1, a2, a3, a4); + } + +private: + FuncPtr<R(B0)> _f; + FuncPtr<B0(A0, A1, A2, A3, A4)> _g; +}; + +/** Static function composition + */ template <typename R, typename B0, typename A0, typename A1, typename A2, typename A3> class Composer<R(B0), B0(A0, A1, A2, A3)> { public: @@ -37,7 +133,7 @@ /** Create a Composer, composing functions and methods */ template <typename GT, typename GM> - Composer(FuncPtr<B0(A0, A1, A2, A3)> f, GT *gobj, GM gmethod) { + Composer(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { attach(f, gobj, gmethod); } @@ -133,7 +229,7 @@ /** Create a Composer, composing functions and methods */ template <typename GT, typename GM> - Composer(FuncPtr<B0(A0, A1, A2)> f, GT *gobj, GM gmethod) { + Composer(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { attach(f, gobj, gmethod); } @@ -229,7 +325,7 @@ /** Create a Composer, composing functions and methods */ template <typename GT, typename GM> - Composer(FuncPtr<B0(A0, A1)> f, GT *gobj, GM gmethod) { + Composer(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { attach(f, gobj, gmethod); } @@ -325,7 +421,7 @@ /** Create a Composer, composing functions and methods */ template <typename GT, typename GM> - Composer(FuncPtr<B0(A0)> f, GT *gobj, GM gmethod) { + Composer(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { attach(f, gobj, gmethod); } @@ -421,7 +517,7 @@ /** Create a Composer, composing functions and methods */ template <typename GT, typename GM> - Composer(FuncPtr<B0()> f, GT *gobj, GM gmethod) { + Composer(FuncPtr<R(B0)> f, GT *gobj, GM gmethod) { attach(f, gobj, gmethod); }
diff -r 079c5ad807fb -r a0fde14b6c39 FuncPtr.h --- a/FuncPtr.h Wed Apr 20 02:43:14 2016 -0500 +++ b/FuncPtr.h Sun Apr 17 23:38:04 2016 -0500 @@ -17,6 +17,160 @@ /** Flexible templated function class */ +template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +class FuncPtr<R(A0, A1, A2, A3, A4)> { +public: + /** Create a FuncPtr with a static function + * @param func Static function to attach + */ + FuncPtr(R (*func)(A0, A1, A2, A3, A4) = 0) { + attach(func); + } + + /** Create a FuncPtr with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + */ + template<typename T> + FuncPtr(T *obj, R (*func)(T*, A0, A1, A2, A3, A4)) { + attach(obj, func); + } + + /** Create a FuncPtr with a method + * @param obj Pointer to object to invoke method on + * @param func Method to attach + */ + template<typename T> + FuncPtr(T *obj, R (T::*func)(A0, A1, A2, A3, A4)) { + attach(obj, func); + } + + /** Create a FuncPtr with a function object + * @param func Function object to attach + */ + template<typename T> + FuncPtr(T *obj) { + attach(obj, &T::operator()); + } + + /** Create a FuncPtr with another FuncPtr + * @param func FuncPtr to attach + */ + FuncPtr(const FuncPtr<R(A0, A1, A2, A3, A4)> &func) { + attach(func); + } + + /** Attach a static function + * @param func Static function to attach + */ + void attach(R (*func)(A0, A1, A2, A3, A4)) { + memcpy(&_func, &func, sizeof func); + _thunk = func ? &FuncPtr::_staticthunk : 0; + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + */ + template <typename T> + void attach(T *obj, R (*func)(T*, A0, A1, A2, A3, A4)) { + _obj = static_cast<void*>(obj); + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_boundthunk<T>; + } + + /** Attach a method + * @param obj Pointer to object to invoke method on + * @param func Method to attach + */ + template<typename T> + void attach(T *obj, R (T::*func)(A0, A1, A2, A3, A4)) { + _obj = static_cast<void*>(obj); + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_methodthunk<T>; + } + + /** Attach a function object + * @param func Function object to attach + */ + template<typename T> + void attach(T *func) { + attach(func, &T::operator()); + } + + /** Attach a FuncPtr + * @param func The FuncPtr to attach + */ + void attach(const FuncPtr<R(A0, A1, A2, A3, A4)> &func) { + _obj = func._obj; + memcpy(&_func, &func._func, sizeof _func); + _thunk = func._thunk; + } + + /** Call the attached function + */ + R call(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return _thunk(_obj, &_func, a0, a1, a2, a3, a4); + } + + /** Call the attached function + */ + R operator()(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return call(a0, a1, a2, a3, a4); + } + + /** Test if function has been attached + */ + operator bool() const { + return _thunk; + } + + /** Static thunk for passing as C-style function + * @param func FuncPtr to call passed as void pointer + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return static_cast<FuncPtr<R(A0, A1, A2, A3, A4)>*>(func) + ->call(a0, a1, a2, a3, a4); + } + +private: + // Internal thunks for various function types + static R _staticthunk(void*, void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return (*reinterpret_cast<R (**)(A0, A1, A2, A3, A4)>(func)) + (a0, a1, a2, a3, a4); + } + + template<typename T> + static R _boundthunk(void *obj, void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return (*reinterpret_cast<R (**)(T*, A0, A1, A2, A3, A4)>(func)) + (static_cast<T*>(obj), a0, a1, a2, a3, a4); + } + + template<typename T> + static R _methodthunk(void *obj, void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return (static_cast<T*>(obj)->* + (*reinterpret_cast<R (T::**)(A0, A1, A2, A3, A4)>(func))) + (a0, a1, a2, a3, a4); + } + + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class *); + void (_class::*_methodfunc)(); + } _func; + + void *_obj; + + // Thunk registered on attach to dispatch calls + R (*_thunk)(void*, void*, A0, A1, A2, A3, A4); +}; + +/** Flexible templated function class + */ template <typename R, typename A0, typename A1, typename A2, typename A3> class FuncPtr<R(A0, A1, A2, A3)> { public: