5

I'd like to be able to write my ISR in one place:

some_collection TimerHandlers;    

// added to ISR table in linker script
void rawTimerIRQHandler() {
    call_each_handler_in(handlers);
}

Such that I can then register handlers in other files

// file1.cpp
void ledTimerHandler1() {

}
register(ledTimerHandler1); //or in an init function if not possible here
// file2.cpp
void ledTimerHandler2() {

}
register(ledTimerHandler2); //or in an init function if not possible here

And when the hardware jumps to rawTimerIRQHandler, it executes ledTimerHandler1 and ledTimerHandler2 in some arbitrary order.


Obviously, I can implement this using something similar to a vector<void(*)()>, but since the number of these handlers is known at compile-time, is there any way I can generate an array (or template linked list) at compile-time? I'd like to avoid the dynamic memory allocation that comes with vector.

I'm open to using template<>, #define, or even GCC-specific attributes to acheive this goal.

Eric
  • 95,302
  • 53
  • 242
  • 374

3 Answers3

4

The scaffolding's a bit tedious but once it's done the usage couldn't be simpler:

// example.h:
#include "Registered.h"
struct example : Registered<example> {};
// main.cc:
#include <iostream>
#include "example.h"

int main ()
{
    for ( auto p = example::registry; p; p=p->chain )
        std::cout << p << '\n';
}
// Registered.h : 
template<class registered>
struct Registered {
     static registered *registry;
     registered *chain;
     Registered() : chain(registry) {registry=static_cast<registered*>(this);}
};
// example.cc:
#include "example.h"
template<> example *Registered<example>::registry = 0;

static struct example first, second, third;  // these can be defined anywhere w/ static duration

edit: moved the first,second,third declaration/definitions to satisfy my inner pedant

jthill
  • 55,082
  • 5
  • 77
  • 137
  • Very clever! I'm not sure why I hadn't seen static fields and constructors as the way to create global initial state. I was already heading towards linked lists. I'll accept this once I've got it working in my context. – Eric Feb 28 '13 at 18:35
  • Is it possible to move the `template<> example *Registered::registry = 0;` to inside the definition of the template in `Registered.h`? – Eric Feb 28 '13 at 18:38
  • Also, presumably I could use this in a non-static context, and make each element remove itself in the restrictor. – Eric Feb 28 '13 at 23:46
1

Absolutley. If I understand correctly, you just want a fixed array of function pointers to your handlers. Using C++11 syntax, and assuming 3 handlers just for the sake of the example,

#include <array>

const std::array<HandlerPtr, 3> handlers= {&ledTimerHandler1, &ledTimerHandler2, &ledTimerHandler3};

or using more classic C/C++ syntax

const HandlerPtr handlers[] = {&ledTimerHandler1, &ledTimerHandler2, &ledTimerHandler3};
Matt Kline
  • 10,149
  • 7
  • 50
  • 87
  • I guess I missed something important here. I'd prefer not to have to manually build the array in one file. My hope was to do some trickery alongside the definition in each file, and have the compiler (or linker) build the array for me. That way, I can exclude tests from the build, and their interrupt handlers are also excluded, without any source modifications. – Eric Feb 28 '13 at 18:00
  • I see. My apologies for not understanding the question. – Matt Kline Feb 28 '13 at 18:00
  • You've probably helped clarify it for others. I was having a hard time putting the question in words. – Eric Feb 28 '13 at 18:02
  • In that case, it would likely take quite a bit of preprocessor or template magic. I know it's not your original stated goal, but is there a reason why you would be against initializing some `vector` as your program starts up? It's not ideal, but it only happens once and after it's initialized you don't suffer any additional overhead. – Matt Kline Feb 28 '13 at 18:05
  • I guess I'm paranoid that the `new` operator will do something stupid. I guess dynamic allocations at startup aren't nearly as bad though. It just feels like the compiler has enough information to allocate just the right amount of memory. – Eric Feb 28 '13 at 18:10
  • Even if this doesn't address the OP's concern it does solve the issue and deserves an upvote. – Nik Bougalis Feb 28 '13 at 18:38
0

Based off jthill's answer, here's what I'll probably end up using (since I don't need a generic form):

struct timer_handler {
    static timer_handler *first = 0;
    timer_handler *next;
    void (*f)();
public:
    timer_handler(void (*f)()) : next(first), f(f) { first = this;}

    // connect this to the interrupt vector
    static inline void executeAll() {
        auto p = first;
        while(p) {
            p->f();
            p = p->next;
        }
    }
};
//a.cpp
void foo() {

}
timer_handler tfoo = foo;
//b.cpp
void bar() {

}
timer_handler tbar = bar;
Eric
  • 95,302
  • 53
  • 242
  • 374