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 23:38:04 2016 -0500
Revision:
18:a0fde14b6c39
Parent:
17:079c5ad807fb
Increase to 5 args

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Christopher Haster 12:a005f8ca5d36 1 /* Platform specific trampoline generation
Christopher Haster 12:a005f8ca5d36 2 */
Christopher Haster 12:a005f8ca5d36 3 #ifndef TRAMPOLINE_H
Christopher Haster 12:a005f8ca5d36 4 #define TRAMPOLINE_H
Christopher Haster 12:a005f8ca5d36 5
Christopher Haster 12:a005f8ca5d36 6
Christopher Haster 12:a005f8ca5d36 7 /* Type of function available from trampolines
Christopher Haster 12:a005f8ca5d36 8 */
Christopher Haster 12:a005f8ca5d36 9 typedef void entry_t();
Christopher Haster 12:a005f8ca5d36 10
Christopher Haster 12:a005f8ca5d36 11 /* Trampoline storage
Christopher Haster 12:a005f8ca5d36 12 */
Christopher Haster 12:a005f8ca5d36 13 typedef struct trampoline trampoline_t;
Christopher Haster 12:a005f8ca5d36 14
Christopher Haster 12:a005f8ca5d36 15 /* Trampoline setup
Christopher Haster 12:a005f8ca5d36 16 * @param trampoline Storage for trampoline
Christopher Haster 12:a005f8ca5d36 17 * @param callback Function to call with context
Christopher Haster 12:a005f8ca5d36 18 * @param context Context to pass to function
Christopher Haster 12:a005f8ca5d36 19 * @return Entry point into trampoline
Christopher Haster 12:a005f8ca5d36 20 */
Christopher Haster 12:a005f8ca5d36 21 static inline entry_t *trampoline_init(trampoline_t *trampoline,
Christopher Haster 12:a005f8ca5d36 22 void (*callback)(void *), void *context);
Christopher Haster 12:a005f8ca5d36 23
Christopher Haster 12:a005f8ca5d36 24
Christopher Haster 12:a005f8ca5d36 25 /* Platform specific trampoline generation
Christopher Haster 12:a005f8ca5d36 26 */
Christopher Haster 12:a005f8ca5d36 27 #if defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__thumb2__)
Christopher Haster 12:a005f8ca5d36 28 struct trampoline {
Christopher Haster 12:a005f8ca5d36 29 volatile uint32_t code[1];
Christopher Haster 12:a005f8ca5d36 30 volatile uint32_t context;
Christopher Haster 12:a005f8ca5d36 31 volatile uint32_t callback;
Christopher Haster 12:a005f8ca5d36 32 };
Christopher Haster 12:a005f8ca5d36 33
Christopher Haster 12:a005f8ca5d36 34 static inline entry_t *trampoline_init(trampoline_t *trampoline,
Christopher Haster 12:a005f8ca5d36 35 void (*callback)(void *), void *context) {
Christopher Haster 12:a005f8ca5d36 36
Christopher Haster 12:a005f8ca5d36 37 // Loads context into r0 and branches to callback
Christopher Haster 12:a005f8ca5d36 38 // Only touches scratch registers saved by caller
Christopher Haster 12:a005f8ca5d36 39 trampoline->code[0] = 0x8001e89f; // ldm pc, {r0, pc}
Christopher Haster 12:a005f8ca5d36 40 trampoline->context = (uint32_t)context;
Christopher Haster 12:a005f8ca5d36 41 trampoline->callback = (uint32_t)callback;
Christopher Haster 12:a005f8ca5d36 42
Christopher Haster 12:a005f8ca5d36 43 // Flush caches
Christopher Haster 12:a005f8ca5d36 44 #if defined(__GNUC__)
Christopher Haster 12:a005f8ca5d36 45 __asm__ volatile ("isb 0xf":::"memory");
Christopher Haster 12:a005f8ca5d36 46 __asm__ volatile ("dsb 0xf":::"memory");
Christopher Haster 12:a005f8ca5d36 47 #else
Christopher Haster 12:a005f8ca5d36 48 __ISB()
Christopher Haster 12:a005f8ca5d36 49 __DSB()
Christopher Haster 12:a005f8ca5d36 50 #endif
Christopher Haster 12:a005f8ca5d36 51
Christopher Haster 12:a005f8ca5d36 52 // Set thumb bit
Christopher Haster 12:a005f8ca5d36 53 return (entry_t *)((uint32_t)trampoline->code + 1);
Christopher Haster 12:a005f8ca5d36 54 }
Christopher Haster 12:a005f8ca5d36 55
Christopher Haster 12:a005f8ca5d36 56 #elif defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__thumb__)
Christopher Haster 12:a005f8ca5d36 57 struct trampoline {
Christopher Haster 12:a005f8ca5d36 58 volatile uint16_t code[4];
Christopher Haster 12:a005f8ca5d36 59 volatile uint32_t context;
Christopher Haster 12:a005f8ca5d36 60 volatile uint32_t callback;
Christopher Haster 12:a005f8ca5d36 61 };
Christopher Haster 12:a005f8ca5d36 62
Christopher Haster 12:a005f8ca5d36 63 static inline entry_t *trampoline_init(trampoline_t *trampoline,
Christopher Haster 12:a005f8ca5d36 64 void (*callback)(void *), void *context) {
Christopher Haster 12:a005f8ca5d36 65
Christopher Haster 12:a005f8ca5d36 66 // Loads context into r0, callback to r1, and branches
Christopher Haster 12:a005f8ca5d36 67 // Only touches scratch registers saved by caller
Christopher Haster 12:a005f8ca5d36 68 trampoline->code[0] = 0x2202; // movs r2, #2
Christopher Haster 12:a005f8ca5d36 69 trampoline->code[1] = 0x447a; // add r2, pc
Christopher Haster 12:a005f8ca5d36 70 trampoline->code[2] = 0xca03; // ldm r2!, {r0, r1}
Christopher Haster 12:a005f8ca5d36 71 trampoline->code[3] = 0x4708; // bx r1
Christopher Haster 12:a005f8ca5d36 72 trampoline->context = (uint32_t)context;
Christopher Haster 12:a005f8ca5d36 73 trampoline->callback = (uint32_t)callback;
Christopher Haster 12:a005f8ca5d36 74
Christopher Haster 12:a005f8ca5d36 75 // Flush caches
Christopher Haster 12:a005f8ca5d36 76 #if defined(__GNUC__)
Christopher Haster 12:a005f8ca5d36 77 __asm__ volatile ("isb 0xf":::"memory");
Christopher Haster 12:a005f8ca5d36 78 __asm__ volatile ("dsb 0xf":::"memory");
Christopher Haster 12:a005f8ca5d36 79 #else
Christopher Haster 12:a005f8ca5d36 80 __ISB()
Christopher Haster 12:a005f8ca5d36 81 __DSB()
Christopher Haster 12:a005f8ca5d36 82 #endif
Christopher Haster 12:a005f8ca5d36 83
Christopher Haster 12:a005f8ca5d36 84 // Set thumb bit
Christopher Haster 12:a005f8ca5d36 85 return (entry_t *)((uint32_t)trampoline->code + 1);
Christopher Haster 12:a005f8ca5d36 86 }
Christopher Haster 12:a005f8ca5d36 87
Christopher Haster 12:a005f8ca5d36 88 #elif defined(__i386__)
Christopher Haster 16:c88d591ae0b4 89 #include <sys/mman.h>
Christopher Haster 16:c88d591ae0b4 90 #include <unistd.h>
Christopher Haster 16:c88d591ae0b4 91
Christopher Haster 12:a005f8ca5d36 92 struct trampoline {
Christopher Haster 12:a005f8ca5d36 93 volatile uint8_t code[24];
Christopher Haster 12:a005f8ca5d36 94 volatile uint32_t context;
Christopher Haster 12:a005f8ca5d36 95 volatile uint32_t callback;
Christopher Haster 12:a005f8ca5d36 96 };
Christopher Haster 12:a005f8ca5d36 97
Christopher Haster 12:a005f8ca5d36 98 static inline entry_t *trampoline_init(trampoline_t *trampoline,
Christopher Haster 12:a005f8ca5d36 99 void (*callback)(void *), void *context) {
Christopher Haster 12:a005f8ca5d36 100
Christopher Haster 12:a005f8ca5d36 101 // Pushes context and calls callback
Christopher Haster 12:a005f8ca5d36 102 // Relative call is used to access pc-relative addresses
Christopher Haster 12:a005f8ca5d36 103 // Only touches scratch registers saved by caller
Christopher Haster 12:a005f8ca5d36 104 volatile uint8_t *c = trampoline->code;
Christopher Haster 12:a005f8ca5d36 105 c[ 0]=0x55; // push %ebp
Christopher Haster 12:a005f8ca5d36 106 c[ 1]=0x89; c[2]=0xe5; // mov %esp, %ebp
Christopher Haster 12:a005f8ca5d36 107 c[ 3]=0xe8; c[4]=0; c[5]=0; c[6]=0; c[7]=0; // call 0
Christopher Haster 12:a005f8ca5d36 108 c[ 8]=0x5a; // pop %edx
Christopher Haster 12:a005f8ca5d36 109 c[ 9]=0x8b; c[10]=0x42; c[11]=16; // mov 16(%edx), %eax
Christopher Haster 12:a005f8ca5d36 110 c[12]=0x50; // push %eax
Christopher Haster 12:a005f8ca5d36 111 c[13]=0x8b; c[14]=0x42; c[15]=20; // mov 20(%edx), %eax
Christopher Haster 12:a005f8ca5d36 112 c[16]=0xff; c[17]=0xd0; // call *%eax
Christopher Haster 12:a005f8ca5d36 113 c[18]=0x89; c[19]=0xec; // mov %ebp, %esp
Christopher Haster 12:a005f8ca5d36 114 c[20]=0x5d; // pop %ebp
Christopher Haster 12:a005f8ca5d36 115 c[21]=0xc3; // ret
Christopher Haster 12:a005f8ca5d36 116 trampoline->context = (uint32_t)context;
Christopher Haster 12:a005f8ca5d36 117 trampoline->callback = (uint32_t)callback;
Christopher Haster 12:a005f8ca5d36 118
Christopher Haster 16:c88d591ae0b4 119 // Mark region as executable
Christopher Haster 16:c88d591ae0b4 120 uintptr_t pagesize = sysconf(_SC_PAGESIZE);
Christopher Haster 16:c88d591ae0b4 121 uintptr_t start = (uintptr_t)trampoline & ~(pagesize-1);
Christopher Haster 16:c88d591ae0b4 122 uintptr_t size = ((uintptr_t)trampoline - start) + sizeof(trampoline_t);
Christopher Haster 16:c88d591ae0b4 123 int err = mprotect((void*)start, size, PROT_READ | PROT_WRITE | PROT_EXEC);
Christopher Haster 16:c88d591ae0b4 124 if (err) {
Christopher Haster 16:c88d591ae0b4 125 return 0;
Christopher Haster 16:c88d591ae0b4 126 }
Christopher Haster 16:c88d591ae0b4 127
Christopher Haster 12:a005f8ca5d36 128 // Return entry point
Christopher Haster 12:a005f8ca5d36 129 return (entry_t *)trampoline->code;
Christopher Haster 12:a005f8ca5d36 130 }
Christopher Haster 12:a005f8ca5d36 131
Christopher Haster 12:a005f8ca5d36 132 #elif defined(__amd64__)
Christopher Haster 17:079c5ad807fb 133 #include <sys/mman.h>
Christopher Haster 17:079c5ad807fb 134 #include <unistd.h>
Christopher Haster 17:079c5ad807fb 135
Christopher Haster 17:079c5ad807fb 136 struct trampoline {
Christopher Haster 17:079c5ad807fb 137 volatile uint8_t code[24];
Christopher Haster 17:079c5ad807fb 138 volatile uint64_t context;
Christopher Haster 17:079c5ad807fb 139 volatile uint64_t callback;
Christopher Haster 17:079c5ad807fb 140 };
Christopher Haster 17:079c5ad807fb 141
Christopher Haster 17:079c5ad807fb 142 static inline entry_t *trampoline_init(trampoline_t *trampoline,
Christopher Haster 17:079c5ad807fb 143 void (*callback)(void *), void *context) {
Christopher Haster 17:079c5ad807fb 144
Christopher Haster 17:079c5ad807fb 145 // Pushes context and calls callback
Christopher Haster 17:079c5ad807fb 146 // Relative call is used to access pc-relative addresses
Christopher Haster 17:079c5ad807fb 147 // Only touches scratch registers saved by caller
Christopher Haster 17:079c5ad807fb 148 volatile uint8_t *c = trampoline->code;
Christopher Haster 17:079c5ad807fb 149 c[ 0]=0x55; // push %rbp
Christopher Haster 17:079c5ad807fb 150 c[ 1]=0x48; c[ 2]=0x89; c[ 3]=0xe5; // mov %rsp, %rbp
Christopher Haster 17:079c5ad807fb 151 // mov 13(%rip), %rdi
Christopher Haster 17:079c5ad807fb 152 c[ 4]=0x48; c[ 5]=0x8b; c[ 6]=0x3d; c[ 7]=13; c[ 8]=0; c[ 9]=0; c[10]=0;
Christopher Haster 17:079c5ad807fb 153 // mov 14(%rip), %rax
Christopher Haster 17:079c5ad807fb 154 c[11]=0x48; c[12]=0x8b; c[13]=0x05; c[14]=14; c[15]=0; c[16]=0; c[17]=0;
Christopher Haster 17:079c5ad807fb 155 c[18]=0xff; c[19]=0xd0; // callq *%rax
Christopher Haster 17:079c5ad807fb 156 c[20]=0x5d; // pop %rbp
Christopher Haster 17:079c5ad807fb 157 c[21]=0xc3; // retq
Christopher Haster 17:079c5ad807fb 158 trampoline->context = (uint64_t)context;
Christopher Haster 17:079c5ad807fb 159 trampoline->callback = (uint64_t)callback;
Christopher Haster 17:079c5ad807fb 160
Christopher Haster 17:079c5ad807fb 161 // Mark region as executable
Christopher Haster 17:079c5ad807fb 162 uintptr_t pagesize = sysconf(_SC_PAGESIZE);
Christopher Haster 17:079c5ad807fb 163 uintptr_t start = (uintptr_t)trampoline & ~(pagesize-1);
Christopher Haster 17:079c5ad807fb 164 uintptr_t size = ((uintptr_t)trampoline - start) + sizeof(trampoline_t);
Christopher Haster 17:079c5ad807fb 165 int err = mprotect((void*)start, size, PROT_READ | PROT_WRITE | PROT_EXEC);
Christopher Haster 17:079c5ad807fb 166 if (err) {
Christopher Haster 17:079c5ad807fb 167 return 0;
Christopher Haster 17:079c5ad807fb 168 }
Christopher Haster 17:079c5ad807fb 169
Christopher Haster 17:079c5ad807fb 170 // Return entry point
Christopher Haster 17:079c5ad807fb 171 return (entry_t *)trampoline->code;
Christopher Haster 17:079c5ad807fb 172 }
Christopher Haster 17:079c5ad807fb 173
Christopher Haster 12:a005f8ca5d36 174 #else
Christopher Haster 12:a005f8ca5d36 175 #error "Target is currently not supported"
Christopher Haster 12:a005f8ca5d36 176 #endif
Christopher Haster 12:a005f8ca5d36 177
Christopher Haster 12:a005f8ca5d36 178
Christopher Haster 12:a005f8ca5d36 179 #endif