Electronic – Interrupts using member function

cdevelopmentembeddedfirmwareinterrupts

  • I am trying to use a member function in an interrupt. The function uses member variables so I created a global instance of that class to use it in a static function.
    The problem is that I created a child class that should have the same interrupt. The interrupt is enabled in the constructor of the parent class, so it will be also enabled in the child class but with the wrong instance.

  • To use the interrupt functions in both classes, I put them in a separate header file "Utilities.h" so that I can included it in both source files. But doing void called_from_interrupt () { Instance_1.do_something(); } requires including the class header, which will create multiple definitions errors.

I'll put a short example of what I am trying to do, the original code is really long.

parent_class.h :

#ifndef PARENT_CLASS_H
#define PARENT_CLASS_H

class parent_class {

public :

    parent_class();
    ~parent_class();
    void do_something();
};

extern parent_class instance_1;

#endif

parent_class.cpp :

#ifndef PARENT_CLASS_CPP
#define PARENT_CLASS_CPP

#define LIBCALL_ENABLEINTERRUPT
#define EI_ARDUINO_INTERRUPTED_PIN
#include "EnableInterrupt.h"

#include utilities.h

parent_class instance_1;

parent_class::parent_class() {

    enableInterrupt(PIN_1, called_from_interrupt, FALLING);
}

parent_class::~parent_class() {}

parent_class::do_something() {

    //manipulate some member variables
}

#endif

child_class.h :

#ifndef CHILD_CLASS_H
#define CHILD_CLASS_H

class child_class {

public :

    child_class();
    ~child_class();
};

extern child_class instance_2;

#endif

child_class.cpp :

#ifndef CHILD_CLASS_CPP
#define CHILD_CLASS_CPP

#include utilities.h

child_class instance_2;

child_class::child_class() {}

child_class::~child_class() {}


#endif

utilities.h :

#ifndef UTILITIES_H
#define UTILITIES_H

#include "parent_class.h"

#define PIN_1  10

static void called_from_interrupt() {
    instance_1.do_something();
}

#endif

Do you have please an elegant solution for that ?

Best Answer

Calling the static member of the base class should work.
You just have to make sure the Instance pointer is correct.
This can be done with the singleton pattern.

Be aware of potential reentrancy risk with this approach though.


Example (disclaimer: I have not dealt with this exact problem before): https://godbolt.org/z/a6qj3c

class Base {
protected:
    static Base *instance;
    Base(){}
    virtual ~Base(){}
public:
    static Base* getInstance(){
        return instance;
    }
    static void setInstance(Base* p){
        instance = p;
    }
    virtual void run();
};

Base* Base::instance;

class Child : public Base {
public:
    Child(){
        Base::setInstance(this);
    }
    ~Child() override {
        Base::setInstance(nullptr);
    }
    void run() override;
};

void Base::run(){
    cout << "the base thing" << endl;
}

void Child::run() {
    cout << "the child thing" << endl;
}

extern "C" void isr(){
    if(Base::getInstance() != nullptr)
        Base::getInstance()->run();
}

int main()
{
    Child b;
    isr();
    return 0;
}

Returns:

the child thing

Now, big red flag, you can call Child::run() from two contexts. Your main thread and your interrupt routine. This means that Child::run() can be called while executing Child::run(), this a reentrancy problem.