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: trampoline.h
- Revision:
- 12:a005f8ca5d36
- Child:
- 16:c88d591ae0b4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trampoline.h Sun Apr 17 21:11:07 2016 -0500 @@ -0,0 +1,127 @@ +/* Platform specific trampoline generation + */ +#ifndef TRAMPOLINE_H +#define TRAMPOLINE_H + + +/* Type of function available from trampolines + */ +typedef void entry_t(); + +/* Trampoline storage + */ +typedef struct trampoline trampoline_t; + +/* Trampoline setup + * @param trampoline Storage for trampoline + * @param callback Function to call with context + * @param context Context to pass to function + * @return Entry point into trampoline + */ +static inline entry_t *trampoline_init(trampoline_t *trampoline, + void (*callback)(void *), void *context); + + +/* Platform specific trampoline generation + */ +#if defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__thumb2__) +struct trampoline { + volatile uint32_t code[1]; + volatile uint32_t context; + volatile uint32_t callback; +}; + +static inline entry_t *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 = (uint32_t)context; + trampoline->callback = (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 (entry_t *)((uint32_t)trampoline->code + 1); +} + +#elif defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__thumb__) +struct trampoline { + volatile uint16_t code[4]; + volatile uint32_t context; + volatile uint32_t callback; +}; + +static inline entry_t *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 = (uint32_t)context; + trampoline->callback = (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 (entry_t *)((uint32_t)trampoline->code + 1); +} + +#elif defined(__i386__) +struct trampoline { + volatile uint8_t code[24]; + volatile uint32_t context; + volatile uint32_t callback; +}; + +static inline entry_t *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 = (uint32_t)context; + trampoline->callback = (uint32_t)callback; + + // Return entry point + return (entry_t *)trampoline->code; +} + +#elif defined(__amd64__) +#error "Target is currently not supported" +#else +#error "Target is currently not supported" +#endif + + +#endif