Callback functions / function pointers

Hello

Arduino c/c++ beginner question:

Is this good practice?

I have create a class that reads midi messages from the serial pin. In the main loop the .read() method of my class is constantly called. It checks if serial data is available, reads and collects a complete midi message. The class has a function pointer (is this the correct term?) to which I pass a function. When enough serial data is available the .read() method calls the function.

Here's the code (irrelevant parts removed):

Scetch:

SBMidi midi;

void setup(){
  midi.setCallback(midiCallback);
}

void midiCallback(MidiMessage message)
{
	... Do stuff with incoming message ...
}

void loop() {
  midi.read();
}

SBMidi.h:

class SBMidi
{
	public:
		void setCallback(void (*midiInCallback)(MidiMessage));
	private:
		void (*_midiInCallback)(MidiMessage);
}

SBMidi.cpp

void SBMidi::setCallback(void (*midiInCallback)(MidiMessage))
{
	_midiInCallback = midiInCallback;
}

void SBMidi::read()
{
	...Serial.read() stuff...
		_midiInCallback(message);
}

Cheers

1 Like

Sounds good to me, however you should ask yourself, is this function going to change runtime? If so you need something like this, if not just calling the functione needed is simpler.

That said, experimenting with this kind of constructs is allways learning fun!

Are you planning on registering different callbacks based on certain data ??

In general, there is nothing wrong with this approach. It is overly complicated if you only have a single function that gets called. But, if you have different functions that need to be called it is a good way to have dynamic dispatch.

You must already know some other language; I don't think that many beginners can pickup function pointers and callbacks that easily.

Hello

Thanks!

Yeah, you're right I'm a C# and javascript guy with 10+ years of experience, it's just the c/c++ pointer/reference stuff that is a bit hard, and how it is implemented in the arduino. C# has lots of auto-niceness that lets you do a lot of lazy stuff. And yeah because this is an AVR it's more stripped down.

The reason for the approach is that my implementation will have lots of stuff going on, midi in and out and 60 something buttons and leds, and I want to move the logic away from the cluttry main loop, and I try to do it resuable and nice.

Just wanted to check, because a previous code post was totally dissed because I assumed things would work like c# :slight_smile:

While there's nothing wrong with your example it is a curious blend of C (the function pointer) and C++ (the class). If you're going C++ then a more pure approach would be to define an abstract base class (analogous to a C# interface) and pass a derivation of that to the worker class that would make calls through the base class methods. That should be all recognisable stuff to you as a C# guy.

Alternatively if you want to stick to the function call analogy then the pure c++ way is to use a function object, or functor.

Thanks for the advices.

Well thats one confusing factor. Is this c? c++? avr c?/c++? Arduino c/c++? (Just a rhetorical question)

I'm usually not this knee deep in constructing stuff at this level at my c# dayjob, I mostly use stuff that others have defined, like the .Net framework. And there you would solve stuff like this with event handling... but I guess that's not good if you want to keep resources at a minimum.

So the callback i think came from javascript, it's very nice and simple there. You define a function, and then you pass it's name to another function, and then that function can call it directly, like so (weird example...):

function doStuff(string){
alert(string);
}

function doOtherStuff(callback)
{
callback("hey");
}

doOtherStuff(doStuff);

The compiler w/ Arduino is a C++ compiler.

I don't see your code as mixing C and C++. It was C++ even without the functor paradigm.

Yes I quite like functors. In short, they are a class or struct that implement operator(). Because of this they can be called, and in effect then, you are passing a function as an argument to another function. Plus, they can maintain internal state.

Looking around I found I way to implement a simple callback although I haven't tested (yet) repercussion on the scope it works for public methods…

Let's say you want to read a Serial Output (Stream) and make the context vary accordingly:
// c++ - User callback functions - Arduino Stack Exchange

In your Object header file add:

struct Callback
{
    Callback(void (*f)(void *) = 0, void *d = 0)
    : function(f), data(d) {}
    void (*function)(void *);
    void *data;
};

In your Object declaration (header file):

class myObject
{
    // […]
public:
    // […]
    String ob;// Output buffer
    void callbackBuffer(void *f);
    // […]
};

In your Object methods add (substitute SoftwareSerial by whatever you've declared eg. serialSIM800):

void myObject::callbackBuffer(void *f)
{
    Callback cb;
    while(SoftwareSerial.available())
    {
        char c = SoftwareSerial.read();
        // For example: Process our output buffer at the end of each line
        if(c == '\r' || c == '\n')
        {
            String p=ob;// Copy the output buffer in order to process it because it will be overwritten
            p.trim();//Spaces are useless
            cb = Callback(f,&p);
            cb.function(cb.data);
            ob="";// Clean the buffer
        }
        else ob+=c;
        // Maybe perform some check that your output buffer isn't overflowing and that your data stream isn't timing out…
    }
}

In your ino file you have declared your Object eg. myObject obj and in your loop() when monitoring the serial output and an event occurs call the needed method:

obj.callbackBuffer([](String d)
    {
        // Change, check, perform whatever you need to do with or without the buffer.
        obj.myNeededMethod(d);
    });

You may easely create in your ino file your own function to callback:

myFunction(String d)
{   // Change, check, perform whatever you need to do with or without the buffer.
    obj.myNeededMethod(d);
}

You then call in your loop:

obj.callbackBuffer(myFunction(d));