Add Variadic Parameters to Template Function

Given a pointer to an object and a pointer to a member function defined for that object's class, it's possible to call that function on that object. But, the syntax is torturous and it's easy to mess up:

((objectPtr)->*(memberFunctionPtr))();

So I thought I'd try my hand at writing a generic template function to make it easier. The code below seems to work for any class and any member function regardless of that function's return type. But it's only valid if the member function takes no arguments. Hence my question, can this be made more general with Variadic Parameters so that a member function that takes an arbitrary number of arguments (of arbitrary type) can be called?
Thanks.

CallMemberFunctionWithPointer.h:

#ifndef CALLMEMBERFUNCTIONWITHPOINTER_H_
#define CALLMEMBERFUNCTIONWITHPOINTER_H_

#include "Arduino.h"

template<class RETURN_TYPE, class TARGET_CLASS>
RETURN_TYPE inline callMemberFunction(TARGET_CLASS *objectPtr, RETURN_TYPE (TARGET_CLASS::*memberFunctionPtr)()) {
	return ((objectPtr)->*(memberFunctionPtr))();
}

#endif /* CALLMEMBERFUNCTIONWITHPOINTER_H_ */

Main .ino File:

#include "CallMemberFunctionWithPointer.h"
class TestClass {
public:
  void voidFunction() {
    Serial.println("In voidFunction");
  }

  uint8_t returnUint8_tFunction() {
    Serial.println("In returnUint8_tFunction");
    return 100;
  }
};

TestClass testObject;

void setup() {
  Serial.begin(115200);
  delay(2000);

  callMemberFunction(&testObject, &TestClass::voidFunction);
  Serial.println(callMemberFunction(&testObject, &TestClass::returnUint8_tFunction));
}

void loop() {
}

what about a macro instead of a template ?

#define callMemberFunction(objectPtr, memberFunctionPtr, ...) (((objectPtr))->*((memberFunctionPtr)))(__VA_ARGS__)

class TestClass {
  public:
    void voidFunction() {
      Serial.println("In voidFunction");
    }

    uint8_t returnUint8_tFunction(uint8_t first) {
      Serial.println("In returnUint8_tFunction");
      return first;
    }

    int twoParams(int first, const char * text) {
      Serial.println(text);
      return first;
    }
};

TestClass testObject;

void setup() {
  Serial.begin(115200);
  delay(2000);

  callMemberFunction(&testObject, &TestClass::voidFunction);
  Serial.println(callMemberFunction(&testObject, &TestClass::returnUint8_tFunction, 42));
  Serial.println(callMemberFunction(&testObject, &TestClass::twoParams, 888, "testing with 2 params. Should be returning 888"));

}

void loop() {}

Serial monitor should show

In voidFunction
In returnUint8_tFunction
42
testing with 2 params. Should be returning 888
888

https://godbolt.org/z/hcYbjvqYr

template <class Ret, class T, class... FuncArgs, class... CallArgs>
Ret callMemberFunc(T *inst, Ret (T::*member)(FuncArgs...), CallArgs &&... args) {
    return ((inst)->*(member))(std::forward<CallArgs>(args)...);
} 

You probably also want a const overload.

But if you can, you should use C++ 17's std::invoke instead rather than rolling your own.

2 Likes

Thanks, that is a lot simpler. I in fact was using a macro previously, but it was also only for functions with no parameters. I had forgotten about the "VA_ARGS" capability of macros.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.