What does "void (*)()" mean - pass functions as argument

I want to pass a function as "preProcessor Function" to another function - In my other projects I've done this with the std::bind() function but this is not working in my case (using Arduino ModbusMaster.h)

These functions need a pre/postProcessor function passed:

 void ModbusMaster::preTransmission(void (*)());
 void ModbusMaster::postTransmission(void (*)());

I've created a class for my project and create a new Modbus Object in it:

ModbusMaster _node;

Now I want to use the "normal" post and preprocessor functions used by Arduino ModbusMaster Lib but if I want to pass my functions:

void OR_WE::preTransmission(void)
{
    digitalWrite(this->derePin, 1);
}
void OR_WE::postTransmission(void)
{
    digitalWrite(this->derePin, 0);
}

to the ModbusMaster Object, the following error appears if I want to use std::bind()

void OR_WE::begin(Stream &serial, uint8_t slave)
{
  _node.begin(slave, serial);

  if(this->manualDere)
  {
    _node.postTransmission(postTransmission); //normal passing
    _node.preTransmission(std::bind(&OR_WE::preTransmission, this)); //binded
  }
}

Error:

Error (postTransmission): .pio\libdeps\nodemcu\OR_WE_Energy_Meter\src\OR_WE.cpp:36:28: error: invalid use of non-static member function 'void OR_WE::postTransmission()'

Error (preTransmission): .pio\libdeps\nodemcu\OR_WE_Energy_Meter\src\OR_WE.cpp:37:36: error: cannot convert 'std::_Bind_helper<false, void (OR_WE::*)(), OR_WE*>::type' to 'void (*)()'

When comparing with my other Projects I've seen that there is a difference btw. the arguments definition:

The ModbusMaster Libary defined as Argument:

    void preTransmission(void (*)());
    void postTransmission(void (*)());

IN CPP File of ModbusMaster Lib:
void ModbusMaster::preTransmission**(void (*preTransmission)())**
void ModbusMaster::postTransmission(void (*postTransmission)())

In my Projects it looks like:

typedef std::function<void()> webService;
void registerNewService(const char* url, webService handler, bool restartServerAfterAdd = false);

And there I can pass the function with

std::bind(&className::functionname, this));

I can see a difference but I don't understand it and google is barely a help for me in this case... could anyone explain the difference for me and give a way to pass the function :slight_smile:

My Code is hosted on GitHub if anyone can help me and need more information (I hope that anything relevant is included in my question ^^)
Link: https://github.com/j54j6/OR_WE_Energy_Meter-w-NonAutomaticFC/tree/master/src

--
j54j6

It means "A pointer (the '(*)' part) to a function that takes no arguments (the '()' part, sometimes written as '(void)') which returns no value (the 'void' part)".

You can only use std::bind() if the function you're calling accepts a std::function type as an argument. Apparently the ModbusMaster library doesn't. It's expecting a function pointer. Specifically, it want a pointer to a function that takes no arguments and returns no value, ie ........ void(*)()

What error would that be?

If you make your class global

class MyClass
{
	int pin = 123;

public:

	void MyFnc()
	{
		Serial.println(this->pin);
	}
};

class TheirClass
{
public:
	void TheirFunc(void(*fnc)())
	{
		(*fnc)();
	}
};

MyClass mc;
TheirClass tc;

// the setup function runs once when you press reset or power the board
void setup() 
{
	Serial.begin(115200);
	tc.TheirFunc([]() ->void { mc.MyFnc(); });
}

// the loop function runs over and over again until power down or reset
void loop() {
  
}

@gfvalvo Thank you for the hint, I completly forgot the Error messages :^) - I've added the messages to my question.

Okay, but how can I only pass the Pointer as the argument if I use simply "&postTransmission"
The following Error Message appears:

ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.

@killzone_kid

Thank you for the hint, I've made the methods public, but can you explain this

tc.TheirFunc([]() ->void { mc.MyFnc(); });

for me, it looks like a complete mess for me :smiley: - the last thing I understand is (tc.TheirFunc as method call^^) - It barely looks like a lambda function for me - is that the correct term?
And is there a way that I can do the pointer assignment in the class? - In general I don't what that the user take notice from that function :slight_smile:

Thank you so much for all of your help

Your function uses data stored in instance so you need to call function in a particular instance, this is why you need to bind it somehow with class instance. You can make function static within class then you can pass it for callback without problem, but you wont be able to access non static variables from your class, because again, you need instance for that. You can obtain instance inside your static function by calling some external or another static function that would return instance, but that would be no different from my example in post#5

void ModbusMaster::preTransmission(void (*)());

This function wants void whatever() {...} function as argument. Lambda [](){} is also accepted. So you can wrap your class function in lambda and pass it to ModbusMaster::preTransmission. mc.MyFnc() invokes MyFnc() of the class instance mc, but mc instance has to be global in order to exist inside lambda.

I don't understand why you would need a

void OR_WE::preTransmission(void)

in your .cpp or the

virtual void preTransmission(void);
virtual void postTransmission(void);

in your .h

define your preTransmission callback not as class member, and just use it in your OR_WE class.

consider

subFunc1
subFunc2
subFunc3
void
subFunc1 (void)
{
    Serial.println (__func__);
}

// -------------------------------------

void
subFunc2 (void)
{
    Serial.println (__func__);
}

// -------------------------------------

void
subFunc3 (void)
{
    Serial.println (__func__);
}

// -------------------------------------
void
func (
    void (*f)(void) )
{
    (*f) ();
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    func (subFunc1);
    func (subFunc2);
    func (subFunc3);
}

void loop ()
{
}

The bottom line is there’s no clean way to pass a pointer to a class member function as an argument to another function that’s expecting it’s argument to be ‘void (*)()’. There are various hacks, but they’re all less than satisfying:

  • Use a Lambda Expression as shown by @killzone_kid.

  • Use a regular (free) function in place of the Lambda:

void freeFunction();

class MyClass {
    int pin = 123;

  public:
    void MyFnc() {
      Serial.println(pin);
    }
};

class TheirClass {
  public:
    void TheirFunc(void(*fnc)()) {
      fnc();
    }
};

MyClass mc;
TheirClass tc;

void setup() {
  Serial.begin(115200);
  tc.TheirFunc(freeFunction);
}

void freeFunction() {
  mc.MyFnc();
}

void loop() {}

This method is preferable if your callback function is large and complex because putting such a function inside a Lambda is ugly and messy.

  • Use a static class function:
class TheirClass {
  public:
    void setCallback(void(*fnc)()) {
      fPtr = fnc;
    }

    void invokeCallback() {
      fPtr();
    }

  private:
    void (*fPtr)();
};

class MyClass {
    int pin = 123;

  public:
    void begin(TheirClass &tc) {
      instancePtr = this;
      tc.setCallback(MyFnc);
    }

  private:
    static MyClass *instancePtr;
    static void MyFnc() {
      Serial.println(instancePtr->pin);
    }
};

MyClass * MyClass::instancePtr;

MyClass mc;
TheirClass tc;

void setup() {
  Serial.begin(115200);
  delay(1000);
  mc.begin(tc);
  tc.invokeCallback();
}

void loop() {}