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);
Thunker.h@9:89aeb1297779, 2016-04-17 (annotated)
- Committer:
- Christopher Haster
- Date:
- Sun Apr 17 20:38:53 2016 -0500
- Revision:
- 9:89aeb1297779
- Child:
- 10:086f34a27a8d
Added Thunker class for generating static thunks to bound functions
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Christopher Haster |
9:89aeb1297779 | 1 | /* Thunker |
Christopher Haster |
9:89aeb1297779 | 2 | * Thunk generating class for calling function objects through |
Christopher Haster |
9:89aeb1297779 | 3 | * context-less functions |
Christopher Haster |
9:89aeb1297779 | 4 | */ |
Christopher Haster |
9:89aeb1297779 | 5 | #ifndef THUNKER_H |
Christopher Haster |
9:89aeb1297779 | 6 | #define THUNKER_H |
Christopher Haster |
9:89aeb1297779 | 7 | |
Christopher Haster |
9:89aeb1297779 | 8 | #include "FuncPtr.h" |
Christopher Haster |
9:89aeb1297779 | 9 | |
Christopher Haster |
9:89aeb1297779 | 10 | |
Christopher Haster |
9:89aeb1297779 | 11 | /* Platform specific trampoline generation |
Christopher Haster |
9:89aeb1297779 | 12 | */ |
Christopher Haster |
9:89aeb1297779 | 13 | #if defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__thumb2__) |
Christopher Haster |
9:89aeb1297779 | 14 | typedef struct { |
Christopher Haster |
9:89aeb1297779 | 15 | volatile uint32_t code[1]; |
Christopher Haster |
9:89aeb1297779 | 16 | volatile uint32_t context; |
Christopher Haster |
9:89aeb1297779 | 17 | volatile uint32_t callback; |
Christopher Haster |
9:89aeb1297779 | 18 | } trampoline_t; |
Christopher Haster |
9:89aeb1297779 | 19 | |
Christopher Haster |
9:89aeb1297779 | 20 | void *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
9:89aeb1297779 | 21 | void (*callback)(void *), void *context) { |
Christopher Haster |
9:89aeb1297779 | 22 | |
Christopher Haster |
9:89aeb1297779 | 23 | // Loads context into r0 and branches to callback |
Christopher Haster |
9:89aeb1297779 | 24 | // Only touches scratch registers saved by caller |
Christopher Haster |
9:89aeb1297779 | 25 | trampoline->code[0] = 0x8001e89f; // ldm pc, {r0, pc} |
Christopher Haster |
9:89aeb1297779 | 26 | trampoline->context = reinterpret_cast<uint32_t>(context); |
Christopher Haster |
9:89aeb1297779 | 27 | trampoline->callback = reinterpret_cast<uint32_t>(callback); |
Christopher Haster |
9:89aeb1297779 | 28 | |
Christopher Haster |
9:89aeb1297779 | 29 | // Flush caches |
Christopher Haster |
9:89aeb1297779 | 30 | #if defined(__GNUC__) |
Christopher Haster |
9:89aeb1297779 | 31 | __asm__ volatile ("isb 0xf":::"memory"); |
Christopher Haster |
9:89aeb1297779 | 32 | __asm__ volatile ("dsb 0xf":::"memory"); |
Christopher Haster |
9:89aeb1297779 | 33 | #else |
Christopher Haster |
9:89aeb1297779 | 34 | __ISB() |
Christopher Haster |
9:89aeb1297779 | 35 | __DSB() |
Christopher Haster |
9:89aeb1297779 | 36 | #endif |
Christopher Haster |
9:89aeb1297779 | 37 | |
Christopher Haster |
9:89aeb1297779 | 38 | // Set thumb bit |
Christopher Haster |
9:89aeb1297779 | 39 | return reinterpret_cast<void*>(1 | |
Christopher Haster |
9:89aeb1297779 | 40 | reinterpret_cast<uint32_t>(&trampoline->code[0])); |
Christopher Haster |
9:89aeb1297779 | 41 | } |
Christopher Haster |
9:89aeb1297779 | 42 | |
Christopher Haster |
9:89aeb1297779 | 43 | #elif defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__thumb__) |
Christopher Haster |
9:89aeb1297779 | 44 | typedef struct { |
Christopher Haster |
9:89aeb1297779 | 45 | volatile uint16_t code[4]; |
Christopher Haster |
9:89aeb1297779 | 46 | volatile uint32_t context; |
Christopher Haster |
9:89aeb1297779 | 47 | volatile uint32_t callback; |
Christopher Haster |
9:89aeb1297779 | 48 | } trampoline_t; |
Christopher Haster |
9:89aeb1297779 | 49 | |
Christopher Haster |
9:89aeb1297779 | 50 | void *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
9:89aeb1297779 | 51 | void (*callback)(void *), void *context) { |
Christopher Haster |
9:89aeb1297779 | 52 | |
Christopher Haster |
9:89aeb1297779 | 53 | // Loads context into r0, callback to r1, and branches |
Christopher Haster |
9:89aeb1297779 | 54 | // Only touches scratch registers saved by caller |
Christopher Haster |
9:89aeb1297779 | 55 | trampoline->code[0] = 0x2202; // movs r2, #2 |
Christopher Haster |
9:89aeb1297779 | 56 | trampoline->code[1] = 0x447a; // add r2, pc |
Christopher Haster |
9:89aeb1297779 | 57 | trampoline->code[2] = 0xca03; // ldm r2!, {r0, r1} |
Christopher Haster |
9:89aeb1297779 | 58 | trampoline->code[3] = 0x4708; // bx r1 |
Christopher Haster |
9:89aeb1297779 | 59 | trampoline->context = reinterpret_cast<uint32_t>(context); |
Christopher Haster |
9:89aeb1297779 | 60 | trampoline->callback = reinterpret_cast<uint32_t>(callback); |
Christopher Haster |
9:89aeb1297779 | 61 | |
Christopher Haster |
9:89aeb1297779 | 62 | // Flush caches |
Christopher Haster |
9:89aeb1297779 | 63 | #if defined(__GNUC__) |
Christopher Haster |
9:89aeb1297779 | 64 | __asm__ volatile ("isb 0xf":::"memory"); |
Christopher Haster |
9:89aeb1297779 | 65 | __asm__ volatile ("dsb 0xf":::"memory"); |
Christopher Haster |
9:89aeb1297779 | 66 | #else |
Christopher Haster |
9:89aeb1297779 | 67 | __ISB() |
Christopher Haster |
9:89aeb1297779 | 68 | __DSB() |
Christopher Haster |
9:89aeb1297779 | 69 | #endif |
Christopher Haster |
9:89aeb1297779 | 70 | |
Christopher Haster |
9:89aeb1297779 | 71 | // Set thumb bit |
Christopher Haster |
9:89aeb1297779 | 72 | return reinterpret_cast<void*>(1 | |
Christopher Haster |
9:89aeb1297779 | 73 | reinterpret_cast<uint32_t>(&trampoline->code[0])); |
Christopher Haster |
9:89aeb1297779 | 74 | } |
Christopher Haster |
9:89aeb1297779 | 75 | |
Christopher Haster |
9:89aeb1297779 | 76 | #elif defined(__i386__) |
Christopher Haster |
9:89aeb1297779 | 77 | struct trampoline_t { |
Christopher Haster |
9:89aeb1297779 | 78 | volatile uint8_t code[24]; |
Christopher Haster |
9:89aeb1297779 | 79 | volatile uint32_t context; |
Christopher Haster |
9:89aeb1297779 | 80 | volatile uint32_t callback; |
Christopher Haster |
9:89aeb1297779 | 81 | }; |
Christopher Haster |
9:89aeb1297779 | 82 | |
Christopher Haster |
9:89aeb1297779 | 83 | void *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
9:89aeb1297779 | 84 | void (*callback)(void *), void *context) { |
Christopher Haster |
9:89aeb1297779 | 85 | |
Christopher Haster |
9:89aeb1297779 | 86 | // Pushes context and calls callback |
Christopher Haster |
9:89aeb1297779 | 87 | // Relative call is used to access pc-relative addresses |
Christopher Haster |
9:89aeb1297779 | 88 | // Only touches scratch registers saved by caller |
Christopher Haster |
9:89aeb1297779 | 89 | volatile uint8_t *c = trampoline->code; |
Christopher Haster |
9:89aeb1297779 | 90 | c[ 0]=0x55; // push %ebp |
Christopher Haster |
9:89aeb1297779 | 91 | c[ 1]=0x89; c[2]=0xe5; // mov %esp, %ebp |
Christopher Haster |
9:89aeb1297779 | 92 | c[ 3]=0xe8; c[4]=0; c[5]=0; c[6]=0; c[7]=0; // call 0 |
Christopher Haster |
9:89aeb1297779 | 93 | c[ 8]=0x5a; // pop %edx |
Christopher Haster |
9:89aeb1297779 | 94 | c[ 9]=0x8b; c[10]=0x42; c[11]=16; // mov 16(%edx), %eax |
Christopher Haster |
9:89aeb1297779 | 95 | c[12]=0x50; // push %eax |
Christopher Haster |
9:89aeb1297779 | 96 | c[13]=0x8b; c[14]=0x42; c[15]=20; // mov 20(%edx), %eax |
Christopher Haster |
9:89aeb1297779 | 97 | c[16]=0xff; c[17]=0xd0; // call *%eax |
Christopher Haster |
9:89aeb1297779 | 98 | c[18]=0x89; c[19]=0xec; // mov %ebp, %esp |
Christopher Haster |
9:89aeb1297779 | 99 | c[20]=0x5d; // pop %ebp |
Christopher Haster |
9:89aeb1297779 | 100 | c[21]=0xc3; // ret |
Christopher Haster |
9:89aeb1297779 | 101 | trampoline->context = reinterpret_cast<uint32_t>(context); |
Christopher Haster |
9:89aeb1297779 | 102 | trampoline->callback = reinterpret_cast<uint32_t>(callback); |
Christopher Haster |
9:89aeb1297779 | 103 | |
Christopher Haster |
9:89aeb1297779 | 104 | // Return entry point |
Christopher Haster |
9:89aeb1297779 | 105 | return const_cast<uint8_t*>(trampoline->code); |
Christopher Haster |
9:89aeb1297779 | 106 | } |
Christopher Haster |
9:89aeb1297779 | 107 | |
Christopher Haster |
9:89aeb1297779 | 108 | #elif defined(__amd64__) |
Christopher Haster |
9:89aeb1297779 | 109 | #error "Target is currently not supported" |
Christopher Haster |
9:89aeb1297779 | 110 | #else |
Christopher Haster |
9:89aeb1297779 | 111 | #error "Target is currently not supported" |
Christopher Haster |
9:89aeb1297779 | 112 | #endif |
Christopher Haster |
9:89aeb1297779 | 113 | |
Christopher Haster |
9:89aeb1297779 | 114 | |
Christopher Haster |
9:89aeb1297779 | 115 | /** Thunk generating class for calling function objects through |
Christopher Haster |
9:89aeb1297779 | 116 | * context-less functions |
Christopher Haster |
9:89aeb1297779 | 117 | */ |
Christopher Haster |
9:89aeb1297779 | 118 | class Thunker { |
Christopher Haster |
9:89aeb1297779 | 119 | public: |
Christopher Haster |
9:89aeb1297779 | 120 | /** Create a Thunker around a function |
Christopher Haster |
9:89aeb1297779 | 121 | */ |
Christopher Haster |
9:89aeb1297779 | 122 | Thunker(FuncPtr<void()> func = 0) { |
Christopher Haster |
9:89aeb1297779 | 123 | attach(func); |
Christopher Haster |
9:89aeb1297779 | 124 | _entry = reinterpret_cast<void(*)()>( |
Christopher Haster |
9:89aeb1297779 | 125 | trampoline_init(&_trampoline, &Thunker::thunk, this)); |
Christopher Haster |
9:89aeb1297779 | 126 | } |
Christopher Haster |
9:89aeb1297779 | 127 | |
Christopher Haster |
9:89aeb1297779 | 128 | /** Create a Thunker around a method |
Christopher Haster |
9:89aeb1297779 | 129 | */ |
Christopher Haster |
9:89aeb1297779 | 130 | template <typename T, typename M> |
Christopher Haster |
9:89aeb1297779 | 131 | Thunker(T *obj, M method) { |
Christopher Haster |
9:89aeb1297779 | 132 | attach(obj, method); |
Christopher Haster |
9:89aeb1297779 | 133 | _entry = reinterpret_cast<void(*)()>( |
Christopher Haster |
9:89aeb1297779 | 134 | trampoline_init(&_trampoline, &Thunker::thunk, this)); |
Christopher Haster |
9:89aeb1297779 | 135 | } |
Christopher Haster |
9:89aeb1297779 | 136 | |
Christopher Haster |
9:89aeb1297779 | 137 | /** Attach a function |
Christopher Haster |
9:89aeb1297779 | 138 | */ |
Christopher Haster |
9:89aeb1297779 | 139 | void attach(FuncPtr<void()> func) { |
Christopher Haster |
9:89aeb1297779 | 140 | _func.attach(func); |
Christopher Haster |
9:89aeb1297779 | 141 | } |
Christopher Haster |
9:89aeb1297779 | 142 | |
Christopher Haster |
9:89aeb1297779 | 143 | /** Attach a method |
Christopher Haster |
9:89aeb1297779 | 144 | */ |
Christopher Haster |
9:89aeb1297779 | 145 | template <typename T, typename M> |
Christopher Haster |
9:89aeb1297779 | 146 | void attach(T *obj, M method) { |
Christopher Haster |
9:89aeb1297779 | 147 | attach(FuncPtr<void()>(obj, method)); |
Christopher Haster |
9:89aeb1297779 | 148 | } |
Christopher Haster |
9:89aeb1297779 | 149 | |
Christopher Haster |
9:89aeb1297779 | 150 | /** Call the attached function |
Christopher Haster |
9:89aeb1297779 | 151 | */ |
Christopher Haster |
9:89aeb1297779 | 152 | void call() { |
Christopher Haster |
9:89aeb1297779 | 153 | return _func(); |
Christopher Haster |
9:89aeb1297779 | 154 | } |
Christopher Haster |
9:89aeb1297779 | 155 | |
Christopher Haster |
9:89aeb1297779 | 156 | /** Call the attached function |
Christopher Haster |
9:89aeb1297779 | 157 | */ |
Christopher Haster |
9:89aeb1297779 | 158 | void operator()() { |
Christopher Haster |
9:89aeb1297779 | 159 | return call(); |
Christopher Haster |
9:89aeb1297779 | 160 | } |
Christopher Haster |
9:89aeb1297779 | 161 | |
Christopher Haster |
9:89aeb1297779 | 162 | /** Test if function has be attached |
Christopher Haster |
9:89aeb1297779 | 163 | */ |
Christopher Haster |
9:89aeb1297779 | 164 | operator bool() const { |
Christopher Haster |
9:89aeb1297779 | 165 | return _func; |
Christopher Haster |
9:89aeb1297779 | 166 | } |
Christopher Haster |
9:89aeb1297779 | 167 | |
Christopher Haster |
9:89aeb1297779 | 168 | /** Static thunk for passing as C-style function |
Christopher Haster |
9:89aeb1297779 | 169 | * @param func Thunker to call passed as void pointer |
Christopher Haster |
9:89aeb1297779 | 170 | */ |
Christopher Haster |
9:89aeb1297779 | 171 | static void thunk(void *func) { |
Christopher Haster |
9:89aeb1297779 | 172 | return static_cast<Thunker*>(func)->call(); |
Christopher Haster |
9:89aeb1297779 | 173 | } |
Christopher Haster |
9:89aeb1297779 | 174 | |
Christopher Haster |
9:89aeb1297779 | 175 | /** Entry point into thunk into function |
Christopher Haster |
9:89aeb1297779 | 176 | */ |
Christopher Haster |
9:89aeb1297779 | 177 | void (*entry())() { |
Christopher Haster |
9:89aeb1297779 | 178 | return _entry; |
Christopher Haster |
9:89aeb1297779 | 179 | } |
Christopher Haster |
9:89aeb1297779 | 180 | |
Christopher Haster |
9:89aeb1297779 | 181 | /** Entry point into thunk into function |
Christopher Haster |
9:89aeb1297779 | 182 | */ |
Christopher Haster |
9:89aeb1297779 | 183 | (*operator void())() { |
Christopher Haster |
9:89aeb1297779 | 184 | return entry(); |
Christopher Haster |
9:89aeb1297779 | 185 | } |
Christopher Haster |
9:89aeb1297779 | 186 | |
Christopher Haster |
9:89aeb1297779 | 187 | private: |
Christopher Haster |
9:89aeb1297779 | 188 | void (*_entry)(); |
Christopher Haster |
9:89aeb1297779 | 189 | trampoline_t _trampoline; |
Christopher Haster |
9:89aeb1297779 | 190 | FuncPtr<void()> _func; |
Christopher Haster |
9:89aeb1297779 | 191 | }; |
Christopher Haster |
9:89aeb1297779 | 192 | |
Christopher Haster |
9:89aeb1297779 | 193 | |
Christopher Haster |
9:89aeb1297779 | 194 | #endif |