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);
Diff: FuncPtr.h
- Revision:
- 0:176c74479de2
- Child:
- 1:9c28068feddc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FuncPtr.h Sun Apr 17 13:25:33 2016 -0500 @@ -0,0 +1,743 @@ +/** FuncPtr + * Flexible templated function class that avoids dynamic memory + * allocation without limiting functionality + */ +#ifndef FUNCPTR_H +#define FUNCPTR_H + +#include <string.h> +#include <stdint.h> + + +/** FuncPtr class based on template specialization + */ +template <typename F> +class FuncPtr; + +/** Flexible templated function class + */ +template <typename R, typename A0, typename A1, typename A2, typename A3> +class FuncPtr<R(A0, A1, A2, A3)> { +public: + /** Create a FuncPtr with a static function + * @param func Static function to attach + */ + FuncPtr(R (*func)(A0, A1, A2, A3) = 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)) { + 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)) { + 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)> &func) { + attach(func); + } + + /** Attach a static function + * @param func Static function to attach + */ + void attach(R (*func)(A0, A1, A2, A3)) { + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_staticthunk; + } + + /** 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)) { + _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)) { + _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)> &func) { + _obj = func._obj; + memcpy(&_func, &func._func, sizeof _func); + _thunk = func._thunk; + } + + /** Call the attached static or member function + */ + R call(A0 a0, A1 a1, A2 a2, A3 a3) { + return _thunk(_obj, &_func, a0, a1, a2, a3); + } + + /** Call the attached static or member function + */ + R operator()(A0 a0, A1 a1, A2 a2, A3 a3) { + return call(a0, a1, a2, a3); + } + + /** Test if function has been attached + */ + operator bool(void) const { + return static_cast<bool>(_func); + } + +private: + // Internal thunks for various function types + static R _staticthunk(void*, void *func, A0 a0, A1 a1, A2 a2, A3 a3) { + return (*reinterpret_cast<R (**)(A0, A1, A2, A3)>(func)) + (a0, a1, a2, a3); + } + + template<typename T> + static R _boundthunk(void *obj, void *func, A0 a0, A1 a1, A2 a2, A3 a3) { + return (*reinterpret_cast<R (**)(T*, A0, A1, A2, A3)>(func)) + (static_cast<T*>(obj), a0, a1, a2, a3); + } + + template<typename T> + static R _methodthunk(void *obj, void *func, A0 a0, A1 a1, A2 a2, A3 a3) { + return (static_cast<T*>(obj)->* + (*reinterpret_cast<R (T::**)(A0, A1, A2, A3)>(func))) + (a0, a1, a2, a3); + } + + // 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 + void *_obj; + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class *); + void (_class::*_methodfunc)(); + } _func; + + // Thunk registered on attach to dispatch calls + R (*_thunk)(void*, void*, A0, A1, A2, A3); +}; + +/** Flexible templated function class + */ +template <typename R, typename A0, typename A1, typename A2> +class FuncPtr<R(A0, A1, A2)> { +public: + /** Create a FuncPtr with a static function + * @param func Static function to attach + */ + FuncPtr(R (*func)(A0, A1, A2) = 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)) { + 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)) { + attach(obj, func); + } + + /** Create a FuncPtr with a function object + * @param func Function object to attach + */ + template<typename T> + FuncPtr(T *func) { + attach(func, &T::operator()); + } + + /** Create a FuncPtr with another FuncPtr + * @param func FuncPtr to attach + */ + FuncPtr(const FuncPtr<R(A0, A1, A2)> &func) { + attach(func); + } + + /** Attach a static function + * @param func Static function to attach + */ + void attach(R (*func)(A0, A1, A2)) { + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_staticthunk; + } + + /** 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)) { + _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)) { + _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)> &func) { + _obj = func._obj; + memcpy(&_func, &func._func, sizeof _func); + _thunk = func._thunk; + } + + /** Call the attached static or member function + */ + R call(A0 a0, A1 a1, A2 a2) { + return _thunk(_obj, &_func, a0, a1, a2); + } + + /** Call the attached static or member function + */ + R operator()(A0 a0, A1 a1, A2 a2) { + return call(a0, a1, a2); + } + + /** Test if function has been attached + */ + operator bool(void) const { + return static_cast<bool>(_func); + } + +private: + // Internal thunks for various function types + static R _staticthunk(void*, void *func, A0 a0, A1 a1, A2 a2) { + return (*reinterpret_cast<R (**)(A0, A1, A2)>(func)) + (a0, a1, a2); + } + + template<typename T> + static R _boundthunk(void *obj, void *func, A0 a0, A1 a1, A2 a2) { + return (*reinterpret_cast<R (**)(T*, A0, A1, A2)>(func)) + (static_cast<T*>(obj), a0, a1, a2); + } + + template<typename T> + static R _methodthunk(void *obj, void *func, A0 a0, A1 a1, A2 a2) { + return (static_cast<T*>(obj)->* + (*reinterpret_cast<R (T::**)(A0, A1, A2)>(func))) + (a0, a1, a2); + } + + // 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 + void *_obj; + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class *); + void (_class::*_methodfunc)(); + } _func; + + // Thunk registered on attach to dispatch calls + R (*_thunk)(void*, void*, A0, A1, A2); +}; + +/** Flexible templated function class + */ +template <typename R, typename A0, typename A1> +class FuncPtr<R(A0, A1)> { +public: + /** Create a FuncPtr with a static function + * @param func Static function to attach + */ + FuncPtr(R (*func)(A0, A1) = 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)) { + 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)) { + attach(obj, func); + } + + /** Create a FuncPtr with a function object + * @param func Function object to attach + */ + template<typename T> + FuncPtr(T *func) { + attach(func, &T::operator()); + } + + /** Create a FuncPtr with another FuncPtr + * @param func FuncPtr to attach + */ + FuncPtr(const FuncPtr<R(A0, A1)> &func) { + attach(func); + } + + /** Attach a static function + * @param func Static function to attach + */ + void attach(R (*func)(A0, A1)) { + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_staticthunk; + } + + /** 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)) { + _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)) { + _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)> &func) { + _obj = func._obj; + memcpy(&_func, &func._func, sizeof _func); + _thunk = func._thunk; + } + + /** Call the attached static or member function + */ + R call(A0 a0, A1 a1) { + return _thunk(_obj, &_func, a0, a1); + } + + /** Call the attached static or member function + */ + R operator()(A0 a0, A1 a1) { + return call(a0, a1); + } + + /** Test if function has been attached + */ + operator bool(void) const { + return static_cast<bool>(_func); + } + +private: + // Internal thunks for various function types + static R _staticthunk(void*, void *func, A0 a0, A1 a1) { + return (*reinterpret_cast<R (**)(A0, A1)>(func)) + (a0, a1); + } + + template<typename T> + static R _boundthunk(void *obj, void *func, A0 a0, A1 a1) { + return (*reinterpret_cast<R (**)(T*, A0, A1)>(func)) + (static_cast<T*>(obj), a0, a1); + } + + template<typename T> + static R _methodthunk(void *obj, void *func, A0 a0, A1 a1) { + return (static_cast<T*>(obj)->* + (*reinterpret_cast<R (T::**)(A0, A1)>(func))) + (a0, a1); + } + + // 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 + void *_obj; + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class *); + void (_class::*_methodfunc)(); + } _func; + + // Thunk registered on attach to dispatch calls + R (*_thunk)(void*, void*, A0, A1); +}; + +/** Flexible templated function class + */ +template <typename R, typename A0> +class FuncPtr<R(A0)> { +public: + /** Create a FuncPtr with a static function + * @param func Static function to attach + */ + FuncPtr(R (*func)(A0) = 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)) { + 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)) { + attach(obj, func); + } + + /** Create a FuncPtr with a function object + * @param func Function object to attach + */ + template<typename T> + FuncPtr(T *func) { + attach(func, &T::operator()); + } + + /** Create a FuncPtr with another FuncPtr + * @param func FuncPtr to attach + */ + FuncPtr(const FuncPtr<R(A0)> &func) { + attach(func); + } + + /** Attach a static function + * @param func Static function to attach + */ + void attach(R (*func)(A0)) { + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_staticthunk; + } + + /** 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)) { + _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)) { + _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)> &func) { + _obj = func._obj; + memcpy(&_func, &func._func, sizeof _func); + _thunk = func._thunk; + } + + /** Call the attached static or member function + */ + R call(A0 a0) { + return _thunk(_obj, &_func, a0); + } + + /** Call the attached static or member function + */ + R operator()(A0 a0) { + return call(a0); + } + + /** Test if function has been attached + */ + operator bool(void) const { + return static_cast<bool>(_func); + } + +private: + // Internal thunks for various function types + static R _staticthunk(void*, void *func, A0 a0) { + return (*reinterpret_cast<R (**)(A0)>(func)) + (a0); + } + + template<typename T> + static R _boundthunk(void *obj, void *func, A0 a0) { + return (*reinterpret_cast<R (**)(T*, A0)>(func)) + (static_cast<T*>(obj), a0); + } + + template<typename T> + static R _methodthunk(void *obj, void *func, A0 a0) { + return (static_cast<T*>(obj)->* + (*reinterpret_cast<R (T::**)(A0)>(func))) + (a0); + } + + // 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 + void *_obj; + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class *); + void (_class::*_methodfunc)(); + } _func; + + // Thunk registered on attach to dispatch calls + R (*_thunk)(void*, void*, A0); +}; + +/** Flexible templated function class + */ +template <typename R> +class FuncPtr<R()> { +public: + /** Create a FuncPtr with a static function + * @param func Static function to attach + */ + FuncPtr(R (*func)() = 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*)) { + 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)()) { + attach(obj, func); + } + + /** Create a FuncPtr with a function object + * @param func Function object to attach + */ + template<typename T> + FuncPtr(T *func) { + attach(func, &T::operator()); + } + + /** Create a FuncPtr with another FuncPtr + * @param func FuncPtr to attach + */ + FuncPtr(const FuncPtr<R()> &func) { + attach(func); + } + + /** Attach a static function + * @param func Static function to attach + */ + void attach(R (*func)()) { + memcpy(&_func, &func, sizeof func); + _thunk = &FuncPtr::_staticthunk; + } + + /** 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*)) { + _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)()) { + _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()> &func) { + _obj = func._obj; + memcpy(&_func, &func._func, sizeof _func); + _thunk = func._thunk; + } + + /** Call the attached static or member function + */ + R call() { + return _thunk(_obj, &_func); + } + + /** Call the attached static or member function + */ + R operator()() { + return call(); + } + + /** Test if function has been attached + */ + operator bool(void) const { + return static_cast<bool>(_func); + } + +private: + // Internal thunks for various function types + static R _staticthunk(void*, void *func) { + return (*reinterpret_cast<R (**)()>(func)) + (); + } + + template<typename T> + static R _boundthunk(void *obj, void *func) { + return (*reinterpret_cast<R (**)(T*)>(func)) + (static_cast<T*>(obj)); + } + + template<typename T> + static R _methodthunk(void *obj, void *func) { + return (static_cast<T*>(obj)->* + (*reinterpret_cast<R (T::**)()>(func))) + (); + } + + // 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 + void *_obj; + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class *); + void (_class::*_methodfunc)(); + } _func; + + // Thunk registered on attach to dispatch calls + R (*_thunk)(void*, void*); +}; + + +#endif