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

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