Passing a function as argument to be used in an interrupt

gfvalvo:
My question isn’t about attachInterrupt(). I simply want to provide a way to specify a class that has a call back feature for each instance. The call back function provided to each instance (either in the constructor or with an attach() method) happens to be an instance method of a different class rather than a "regular" function. Look at my pseudo code again:

Each instance of Class1 needs to be provided with a call back function. I want that call back function for instance1 (of Class1) to be the instanceFunction() of instance2 (of Class2).

Since both instances have identical functions (methods) I don't know what you gain unless you want to do something to A based on the state of B. I showed you how to perform just that.

if A and B both call the same callback, then as Paul mentioned, a static function works for that.

BulldogLowell:
I don't know what you gain

Putting that aside, and I understand there are other ways. My question remains, is it possible to do it the way I outlined in my pseudo code? I'm looking for any of the following:

"Yes."
"Yes, and here's how"
"No."
"No, and here's why"
"I don't know"

gfvalvo:
Putting that aside, and I understand there are other ways. My question remains, is it possible to do it the way I outlined in my pseudo code? I'm looking for any of the following:

"Yes."
"Yes, and here's how"
"No."
"No, and here's why"
"I don't know"

in C++11 (as far as I know) there is no way to get a pointer to a class member function.

There may be ways e.g. using Boost or other advanced C++ libraries, but you'd have to investigate that.

I cannot imagine a scenario where you would need a pointer to a member function, so for me my interest is academic.

BulldogLowell:
in C++11 (as far as I know) there is no way to get a pointer to a class member function.

Good enough. Thanks.

Looks like Boost may indeed be the answer. See get-a-pointer-to-objects-member-function

wildbill:
Looks like Boost may indeed be the answer. See get-a-pointer-to-objects-member-function

I've never used Boost for arduino, and haven't seen anyone on the forum, either.

:wink:

Touché

If it is OK for Class1 to know the name of the desired instance function in Class2 then you can just store a reference to the instance, rather than a pointer to a function of that instance. This compiles, but I have not tried running it.

class Class2 {
  public:
    void instanceFunction() {}
};


class Class1 {
  public:
    Class1(Class2 &cbiPtr): _callBackInstance(cbiPtr) {}
    void call() {
      _callBackInstance.instanceFunction();
    }
  private:
    Class2 &_callBackInstance;
};


Class2 instance2;
Class1 instance1(instance2);


void setup() {
  instance1.call();
}


void loop() {}

If Class2 is not the only class that Class1 has to handle, just have all of the classes derive from a base class that Class1 can deal with.

Yea. Some posts on SO and other places do purport to show a way of getting a pointer to an instance method. And, maybe this Boost thing will do it. But, they all seem convoluted at best.

So, once you resign yourself to that, you can move on to passing in an object reference as you an @Bulldog showed. To handle passing different classes, can go with Base / Derived class model or use templates.

Finally, just because I can, I’d then overload the () operator.

Thanks all.

gfvalvo:
I specified precisely which dog:

Class1 instance1(instance2.instanceFunction);

Doesn't that uniquely identify which function to call?

Unfortunately for you, that's not how member function pointers work.

Even more unfortunately, explaining how they work is not a trivial task. A lot of posts here are explaining why your ideas won't work, but there's precious few giving good alternatives.

There is a way to do what you want. The C++ Standard Template Library has a function<> template object in it that can store references to any callable function, and even a bind() function that lets you fix function arguments for a call (so you can bind a member function to a specific instance, for example). You can use a function<void(void)> object instead of a function pointer for much more versatility.

Unfortunately (sensing a theme here?), it doesn't look like the function class is implemented in the ArduinoSTL, at least not that I can see. Trust me when I saw that recreating that functionality will take significant amounts of template and polymorphism wizardry.

The easiest thing to do is to be a little more indirect. Rather than worry about binding the member function to the object instance directly in the function reference, create a normal function to wrap around that member call and give a pointer to that function to the ISR.

Foo* ZhuLi;

void setIntCallbackObjectReference( Foo* const bar )
{
    ZhuLi = bar;
}

// give this function to your interrupt method.
void IntCallbackFunc()
{
    // always test the pointer like this so you never dereference one that points to 0.
    if( ZhuLi != 0 ) ZhuLi->DoTheThing();
}

Jiggy-Ninja:
There is a way to do what you want. The C++ Standard Template Library has a function<> template object in it that can store references to any callable function, and even a bind() function that lets you fix function arguments for a call (so you can bind a member function to a specific instance, for example). You can use a function<void(void)> object instead of a function pointer for much more versatility
The easiest thing to do is to be a little more indirect. Rather than worry about binding the member function to the object instance directly in the function reference, create a normal function to wrap around that member call and give a pointer to that function to the ISR.

Foo* ZhuLi;

void setIntCallbackObjectReference( Foo* const bar )
{
    ZhuLi = bar;
}

// give this function to your interrupt method.
void IntCallbackFunc()
{
    // always test the pointer like this so you never dereference one that points to 0.
    if( ZhuLi != 0 ) ZhuLi->DoTheThing();
}

Was shown way back

Thanks for your persistence, I think I understand now.

The only issue I have is that when I make the ISR static I cannot call any LoRaClass member functions which I need as the callback function is a member of the class.

If I use your first work-around and set callback as the ISR function rather than onDio0Rise, the users function will run within the interrupt, preventing other vital communication interrupts running while callback is operating.

Hence I need something like this with multiple functions to exit the interrupt as fast as possible and then to ensure that the users function (which could be anything) runs ok:

class MyCass{
public:
   void(*userDefinedFunction);   //A receive routine that is lengthy and must run every interrupt

   void setupISR(void(*userDefinedFunction)){
       attatchIntterupt(somePin, ISRFunction, RISING);
   }

  ISRFunction(){
      MyCass::SomeFunction();    //A function to be called in order to exit the interrupt, but this function is itself a member of myClass so ISRFunction cannot be static
  }

  SomeFunction(){
    //setup prior to userfunction

    userDefinedFunction();

    //checks after user function
};

myRXFunction(){
   //do complex receive communication stuff


setup{
   setupISR(myRXFunction);
}

The only issue is that the compiler is trying to force me to set the ISRFunction as static

bakerw71:
The only issue I have is that when I make the ISR static I cannot call any LoRaClass member functions which I need as the callback function is a member of the class.

Sure you can:

class myClass {
  private:
    static myClass *instanceIsrPointer;
    static void staticISR() {
      instanceIsrPointer->instanceISR();
    }
    void instanceISR() {
      // Do ISR stuff here
    }

  public:
    static begin(myClass &instance, uint8_t interruptNumber) {
      instanceIsrPointer = &instance;
      attachInterrupt(digitalPinToInterrupt(interruptNumber), staticISR, RISING);
    }
};

myClass * myClass::instanceIsrPointer;
myClass myInstance;

void setup() {
  myClass::begin(myInstance, 1);
}

void loop() {}

gfvalvo:
Sure you can:

class myClass {

private:
    static myClass *instanceIsrPointer;
    static void staticISR() {
      instanceIsrPointer->instanceISR();
    }
    void instanceISR() {
      // Do ISR stuff here
    }

public:
    static begin(myClass &instance, uint8_t interruptNumber) {
      instanceIsrPointer = &instance;
      attachInterrupt(digitalPinToInterrupt(interruptNumber), staticISR, RISING);
    }
};

myClass * myClass::instanceIsrPointer;
myClass myInstance;

void setup() {
  myClass::begin(myInstance, 1);
}

void loop() {}

but if you have two instances?

BulldogLowell:
but if you have two instances?

Then, you only call the static begin() method with the instance that is supposed to deal with the interrupt.

Or, you modify the class to have a list of instances that are to respond, and the static callback calls the method of all the instances in the list.

PaulS:
Then, you only call the static begin() method with the instance that is supposed to deal with the interrupt.

Or, you modify the class to have a list of instances that are to respond, and the static callback calls the method of all the instances in the list.

I know that, but it wasn't addressed in the example.

go back to #8, if we only need one instance driven from one interrupt pin then all of the rest of the chatter is noise (which is not uncommon here).

go back to #8, if we only need one instance driven from one interrupt pin then all of the rest of the chatter is noise (which is not uncommon here).

If the class was designed as a singleton, then we wouldn't need to be having this conversation. Obviously, the class is (classes are) not designed with that restriction in mind.

PaulS:
Or, you modify the class to have a list of instances that are to respond, and the static callback calls the method of all the instances in the list.

Or uses some other internal / external criteria to determine which of the instance(s) it calls the method on.

BulldogLowell:
I know that, but it wasn't addressed in the example.

A logical extension.

The issue is the instanceISR() in this example must be declared outside of a class, outside of the header in the .ino script otherwise the user of the library must edit the library every time the receive operation is modified rather than changing a function declared in the .ino.

gfvalvo:
Sure you can:

class myClass {

private:
    static myClass *instanceIsrPointer;
    static void staticISR() {
      instanceIsrPointer->instanceISR();
    }
    void instanceISR() {
      // Do ISR stuff here
    }

public:
    static begin(myClass &instance, uint8_t interruptNumber) {
      instanceIsrPointer = &instance;
      attachInterrupt(digitalPinToInterrupt(interruptNumber), staticISR, RISING);
    }
};

myClass * myClass::instanceIsrPointer;
myClass myInstance;

void setup() {
  myClass::begin(myInstance, 1);
}

void loop() {}

I don't see a problem. What am I missing?

myClass.h

#ifndef _MYCLASS_H_
#define _MYCLASS_H_
#include "Arduino.h"

class myClass {
  private:
    static myClass *instanceIsrPointer;
    static void staticISR();
    void instanceISR();

  public:
    static void begin(myClass &, uint8_t);
};

#endif

myClass.cpp

#include "myClass.h"

myClass * myClass::instanceIsrPointer;

void myClass::staticISR() {
  instanceIsrPointer->instanceISR();
}

void myClass::instanceISR() {
  // Do ISR stuff here
}

void myClass::begin(myClass &instance, uint8_t interruptNumber) {
  instanceIsrPointer = &instance;
  attachInterrupt(digitalPinToInterrupt(interruptNumber), staticISR, RISING);
}

.ino file

#include "myClass.h"

myClass myInstance;     //<<<<< Class instance created in .ino file just like you want.

void setup() {
  myClass::begin(myInstance, 1);
}

void loop() {}