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: Thunker.h
- Revision:
- 12:a005f8ca5d36
- Parent:
- 10:086f34a27a8d
- Child:
- 13:4d8a50d4967e
--- a/Thunker.h Sun Apr 17 20:51:55 2016 -0500 +++ b/Thunker.h Sun Apr 17 21:11:07 2016 -0500 @@ -7,110 +7,10 @@ #include "FuncPtr.h" - -/* Platform specific trampoline generation - */ -#if defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__thumb2__) -typedef struct { - volatile uint32_t code[1]; - volatile uint32_t context; - volatile uint32_t callback; -} trampoline_t; - -void *trampoline_init(trampoline_t *trampoline, - void (*callback)(void *), void *context) { - - // Loads context into r0 and branches to callback - // Only touches scratch registers saved by caller - trampoline->code[0] = 0x8001e89f; // ldm pc, {r0, pc} - trampoline->context = reinterpret_cast<uint32_t>(context); - trampoline->callback = reinterpret_cast<uint32_t>(callback); - - // Flush caches -#if defined(__GNUC__) - __asm__ volatile ("isb 0xf":::"memory"); - __asm__ volatile ("dsb 0xf":::"memory"); -#else - __ISB() - __DSB() -#endif - - // Set thumb bit - return reinterpret_cast<void*>(1 | - reinterpret_cast<uint32_t>(&trampoline->code[0])); +extern "C" { +#include "trampoline.h" } -#elif defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__thumb__) -typedef struct { - volatile uint16_t code[4]; - volatile uint32_t context; - volatile uint32_t callback; -} trampoline_t; - -void *trampoline_init(trampoline_t *trampoline, - void (*callback)(void *), void *context) { - - // Loads context into r0, callback to r1, and branches - // Only touches scratch registers saved by caller - trampoline->code[0] = 0x2202; // movs r2, #2 - trampoline->code[1] = 0x447a; // add r2, pc - trampoline->code[2] = 0xca03; // ldm r2!, {r0, r1} - trampoline->code[3] = 0x4708; // bx r1 - trampoline->context = reinterpret_cast<uint32_t>(context); - trampoline->callback = reinterpret_cast<uint32_t>(callback); - - // Flush caches -#if defined(__GNUC__) - __asm__ volatile ("isb 0xf":::"memory"); - __asm__ volatile ("dsb 0xf":::"memory"); -#else - __ISB() - __DSB() -#endif - - // Set thumb bit - return reinterpret_cast<void*>(1 | - reinterpret_cast<uint32_t>(&trampoline->code[0])); -} - -#elif defined(__i386__) -struct trampoline_t { - volatile uint8_t code[24]; - volatile uint32_t context; - volatile uint32_t callback; -}; - -void *trampoline_init(trampoline_t *trampoline, - void (*callback)(void *), void *context) { - - // Pushes context and calls callback - // Relative call is used to access pc-relative addresses - // Only touches scratch registers saved by caller - volatile uint8_t *c = trampoline->code; - c[ 0]=0x55; // push %ebp - c[ 1]=0x89; c[2]=0xe5; // mov %esp, %ebp - c[ 3]=0xe8; c[4]=0; c[5]=0; c[6]=0; c[7]=0; // call 0 - c[ 8]=0x5a; // pop %edx - c[ 9]=0x8b; c[10]=0x42; c[11]=16; // mov 16(%edx), %eax - c[12]=0x50; // push %eax - c[13]=0x8b; c[14]=0x42; c[15]=20; // mov 20(%edx), %eax - c[16]=0xff; c[17]=0xd0; // call *%eax - c[18]=0x89; c[19]=0xec; // mov %ebp, %esp - c[20]=0x5d; // pop %ebp - c[21]=0xc3; // ret - trampoline->context = reinterpret_cast<uint32_t>(context); - trampoline->callback = reinterpret_cast<uint32_t>(callback); - - // Return entry point - return const_cast<uint8_t*>(trampoline->code); -} - -#elif defined(__amd64__) -#error "Target is currently not supported" -#else -#error "Target is currently not supported" -#endif - /** Thunk generating class for calling function objects */ @@ -173,18 +73,18 @@ /** Entry point into thunk into function */ - void (*entry())() { + entry_t *entry() { return _entry; } /** Entry point into thunk into function */ - (*operator void())() { + operator entry_t*() { return entry(); } private: - void (*_entry)(); + entry_t *_entry; trampoline_t _trampoline; FuncPtr<void()> _func; };