Flexible templated function class and related utilities that avoid dynamic memory allocation without limiting functionality

Dependents:   SimpleHTTPExample

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);

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?

UserRevisionLine numberNew 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