Passing a non-static member function, correct syntax ?

Hi,
First post on this forum and thanks in advance for any help received. Apologies if I'm using the incorrect terminology. I want to have a crack at writing my own class/ library code for inputting a number on an LCD using a clickable quadrature rotatory switch. To start with I thought I'd just try to get it to work as a standalone function before I converted it into a class.

My plan was to wrap up a bunch of code into a single function, pass in the myLCD.setCursor and myLCD.print function (where myLCD is an instance of an I2C LCD display, or any display for that matter) and couple of other things like position , message, and size of the number, etc. Then it would do its magic and return a long number.

I can get the code to work if I create wrapper functions around the lcd's .setCursor(x,y) and .print(text) functions, but I'd like to avoid having to create wrapper functions if possible. I'd like to be able to pass in the instances of these methods directly without having to wrap them.

Hopefully some code might make this clearer

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

void PrintWrapper(String text);
void setCursorWrapper(uint8_t col, uint8_t row);
long InputANumber(void (*LCDSetCursorFunction)(uint8_t, uint8_t), uint8_t x_cord, uint8_t y_cord, void (*LCDPrintFunction)(String), String text, int value);
LiquidCrystal_I2C myLCD(0x27, 16, 2);  //Create an instance of the LCD display

void setup() {
  Serial.begin(9600);
  myLCD.init();
  myLCD.backlight();
  myLCD.setCursor(0, 0);
  myLCD.print("Testing...");
}

void loop() {
  //long answer=InputANumber(myLCD.setCursor,  1, 1, myLCD.print,     "A Message", 4);  //------------This Doesn't even Compile--------------
  long answer = InputANumber(setCursorWrapper, 1, 1, LCDPrintWrapper, "A Message", 4);  // ------------This works --------
  // Do Something with the answer
}

// Arguments are the SetCursor function, x cord, y cord, LCDPrint function, Message as a String, Number of digits as an int.
long InputANumber(void (*LCDSetCursorFunction)(uint8_t, uint8_t), uint8_t x, uint8_t y, void (*LCDPrintFunction)(String), String message, int numberOfDigits) {
  (*LCDSetCursorFunction)(x, y);
  delay(1000);
  (*LCDPrintFunction)(message);
  delay(1000);
  // TODO: A bunch of code here that drives a cursor around the LCD screen, 
  // and allows the input of a number of size numberOfDigits
  // using a quadrature clickable rotorary knob
  // Will also need to pass in pin numbers for A & B channel and the push to click input

  // Just return 9999 to make it compile
  return 9999;
}

void setCursorWrapper(uint8_t col, uint8_t row) {
  myLCD.setCursor(col, row);
  Serial.print("Setting cursor position to ");
  Serial.print(col);
  Serial.print(",");
  Serial.println(row);
}

void LCDPrintWrapper(String text) {
  myLCD.print(text);
  Serial.print("Just printed: ");
  Serial.print(text);
  Serial.println(" to the LCD");
}

The code shown above works when the lcd functions are wrapped, but if I uncomment the first line in loop() and comment the second line, it won't compile. It gives an invalid use of non-static member function error.

Is what I'm trying to do possible without creating the wrappers functions?
If so what is the correct syntax for passing in the non static member functions of the myLCD object?
Thanks in advance.
Warren

It is,not really common practice to send functions as arguments. It is,done in menu selection modules.
Why can't the lcd class not be a friend of your getnumber class?
If you even need a getnumber class as it sounds more like,a function to me.
I think you make things more complicated tgan needed. Have you seen the argument list that needs to go with your getnumber function? Not really handy.
If you would make it a class you can also give a pointer to the lcd class ir lcd function during dynamic creation of your getnumber object. Or add it using a,init() function.
I would mot go through all of this. Object oriented programming is supposed to make reuse of code easier... not more complicated.

Hello wdrcomputers

Welcome to the best Arduino forum ever.

My recommendation is to read up on the use of structured arrays.
A structured array contains the handling for the LCD in member variables and member functions.

hth

1 Like

I didn't write the lcd class, its existing library code and my idea was to write something that handled any display, provided the library for the display has 2 methods. One method to position the cursor that takes two ints, another method to print a string to the display at the cursor position.

You are probably right, and I want to get it working as a function initially, the idea of making it into a class was so that I could #include the class and use the code with whatever display I want. I thought this was the way to make your code more readable and more compact.

Yes the argument list is big, but everything is needed to display a message where I want.

Beg to disagree here. But thats my opinion and you are entitled to yours.

Bottom line is, can this be done or not on an arduino? I'm not saying that my approach is the best method but I'd still like to know if it can be done.

Hi Paul,
Thanks for your reply

I'm only a very basic programmer and I'm not sure what you are meaning here. A quick google search on "structured array" doesn't really help me with learning how to pass non-static member functions as parameters. I'll keep reading to see what I can learn.

Although the example code that I provided is about an LCD display and maybe the solution I'm trying to implement appears completely un-logical to some , I'd still like to learn about passing non-static member functions into procedures if this can be done.

1 Like

A class init function could provide access to your lcd.functions. that would also solve the problem where one library uses .write and another .print to do the same thing...

By the way, you do not need to beg to disagree. You can simply disagree. I can only say that I do not often encounter function pointers as function arguments on this forum. It is pretty advanced for most users here. I did use them in my code, but I won't call myself anywhere near experienced with this.

Yes, this is exactly why I want to write this. Different display libraries are going to have different method names. But how does a "class init function" provide access to these functions? At some point I'm going to have to pass in the .setCursor and .print methods (or their functional equivalent methods) aren't I?

When you refer to class init function are you referring to the class I'm trying to create that handles the number input?
And do you mean in the constructor code or in an init method of the class?

Yes.
Could be done both. Up to your taste...

After re-reading these posts I think I can see what you meant, that I should pass in the .setCursor and .print functions into the class only the once (either at construction or though an init method) as it is a very messy process, I shouldn't be passing them on every invocation of the InputANumber method. That makes complete sense.

Then I could call a method of this class that just takes the prompt text, the size of the number and coordinates on the display where to show the number and it would return a long.

That would be a much nicer solution, especially if the InputANumber was used many times in your code. You could also make Y / N prompts that return a boolean in a similar manner.

Having said all that I still need a method of getting these 2 methods into the class so that they can be called by the code inside the class. Hopefully without having to wrap them before hand.

Do you have any sample code of how you did this?

Those were function pointers without arguments that were not stored in a class.
I hope someone else can help you...

You must distinguish between a function and a method. Even if here in the forum both names are often used as synonyms, they are not. For the compiler calling a method is different then calling a function. Your function 'InputANumber' expects a function as parameter. So you cannot give a method as parameter.

The problem is that those non-static member functions are instance methods, and don't work without the instance to which they are bound. In a nuts-and-bolts sense, you need to reserve a few bytes for the reference to the instance. C/C++ is particular about such things.

You can avoid writing a "whole" wrapper function (at least you won't have to think of a name) by using a lambda (or two) that captures the instance.

long answer=InputANumber(
  [&myLCD] (uint8_t col, uint8_t row) {
    myLCD.setCursor(col, row); 
  }, 1, 1, 
  [&myLCD] (String text) {
    myLCD.print(text);
  }, "A Message", 4);

If myLCD is a global variable, the capture can be empty: []

@kenb4 Thanks for your help, I think I get what you are saying.
But what if I want to make many calls to these .setCusor and .print instance methods in the class's code. I understand that these apply to an instance of an LCD object and that is good as there may be many LCD's in the project.

Say for instance I'm using push buttons or a rotary knob to advance or retract the cursor on the LCD display and I'm calling .setCursor and .print many times as the digits are being stepped through from 1 to 9 and advanced from 1000's to 100's to 10's to 1's (for a 4 digit number).

Is there a way to store the member functions of my instance of the LCD object inside the class so that I can repeatable call them from code inside the class. The code I posted at the beginning of this article doesn't have any of this said code yet. I thought it was better to keep it as simple as possible so people would understand my question.

I know I could pass in the whole LCD object and access the LCD's member methods, but then the template code would be hard coded to an I2C LCD object, because I'd have to specify the pointer to an I2C LCD in the parameter list. And it would also be hard coded to the two methods of the I2C LCD object.

What I want to supply as a parameter is an instance method that takes two integers, that I can then call later in the class code.

Maybe what I'm trying to do, just can't be done without wrapping the lcd's instance methods into functions.

Really sorry if this makes no sense, which I'm guessing it probably doesn't, I'm probably trying to do something that can't be done.

Sorry Ken, my lambda stuff is a bit scratchy. I think you have supplied the answer, I just didn't get it.
If i was to use the format that you suggest and then convert this InputANumber function into a full blown class with a constructor and method to return a result. Then I could create some member variable in my class that could hold these functions (ie the .setCursor and .print). As now they are just regular functions as they have been lambda'd which effectively converted them from instance methods into functions.
Is this correct?
If so, what is the type of the member variable that holds these lambdas and how would I call them in code?

A lambda is an anonymous function, and in this case would be used as an anonymous wrapper. You already have the two type declarations

typedef void (*LCDSetCursorFunction)(uint8_t, uint8_t);
typedef void (*LCDPrintFunction)(String);

struct Something {
  LCDSetCursorFunction setCursor;
  LCDPrintFunction print;
  void doBoth(uint8_t a, uint8_t b, String c) {
    setCursor(a, b);
    print(c);
  }
};

void setup() {
  Serial.begin(115200);
  Something s = {
    .setCursor = [] (uint8_t a, uint8_t b) {
      Serial.println(a, b);
    },
    .print = [] (String c) {
      Serial.println(c);
    },
  };
  s.doBoth(356, HEX, "foo");
}

void loop() {
}

A quirk of C is that the usual address-of and dereference operators, & and * are no-ops on a function pointer; and like an array, the name is itself a pointer. But the * is needed (with the parentheses) in the type declaration, which would otherwise create an alias for declaring functions with that given signature.

Thanks @kenb4.
Your response was perfectly timed. I thought I had it all under control. I was just reading up on storing lambda functions in class member variables when I found out that std::function isn't supported when compiling for the Arduino Uno.

I suspect your code is the work around to this issue. At this point I don't fully understand how it works, but I'm going to study your suggested code and see if I can work it out.

I vaguely recall a class being very similar to a struct. So I think I should be able to make it work somehow.

I'm always amazed how complete strangers are willing to help other complete strangers on these forums.
Thanks again for your help.

When you use a c++ template you can easily handover also the class type of the lcd object. You dont need to hardcode the I2c library.

Here is a quick example

By the way: most LCD libs are based on the LCD API 1.0 - so you can expect that the main member functions are compatible.

1 Like

It's done all the time to register callback functions.

That's an impressive simulator.

I haven't really used template classes much, so I'm learning a lot at the moment. But am I correct in assuming that this approach will only work if the display class you template from has a .print method?
I know you said they all should because they are based on the same base, but I'm just trying to understand things here.

So basically the compiler is happy to have the class template code there in the source with a reference to a .print method even though it doesn't know if that code exists, it only checks if the .print code exists if it sees a call to helper.foo and because helper was based on the I2C LCD library, the compiler checks if there is a member function called .print in the I2C LCD library.
Very Interesting.
I suppose it's a bit like prototype definitions. You supply a like of code that says a method or function exists and what type of parameters it takes and the compiler is happy with that even though it hasn't seen the actual code for that method or function yet. And its not until you have a line of code that calls the function declared in the prototype definition that the compiler then checks if that code actually exists.

The example shows basically two things:

  1. how to hand over a reference to an existing object
  2. how to use a template parameter

yes that' correct for this case. The referenced object needs to have all member functions you want to use. Print.h is a very common class. I assume most of the Character-LCD libraries inherit from Print.h - hence it will work.

ye