Go Down

Topic: Issue with passing objects to function (Read 1 time) previous topic - next topic

jeffvader

Hi,

I'm trying to write a generic wait a certain time unless an event happens function where I can pass in the event happens function.  Here's what I've got so far (simplified).

Code: [Select]
boolean wait( unsigned long time,  boolean(*u)(void)) 
{
  return(u());
}


As an aside, I have created a class called Input which does similar stuff to the Button class.  I want to call the wait function like this example.

Code: [Select]
wait( 3000, Modeswitch.on );

Modeswitch is instantiated from Input and on returns true if conditions in the object are met.  I get the following compilation error

error: argument of type 'boolean (Input::)()' does not match 'boolean (*)()'

What am I doing wrong?  I've looked at many tutorials on passing objects/functions and I can't work this out.

Thanks
Jeff

PaulS

Quote
error: argument of type 'boolean (Input::)()' does not match 'boolean (*)()'

I find it hard to believe that the compiler generated an error message with a smiley face in it.

Quote
What am I doing wrong?  I've looked at many tutorials on passing objects/functions and I can't work this out.

For starters, you are not posting any code.

jeffvader

Correct it didn't.  error: argument of type 'boolean (Input : : ) ( ) ' does not match 'boolean (*)()  Added spaces to avoid similes

Here's the code for Input Object - I'm assuming you don't need the header file.
Code: [Select]
/*
  Input.cpp
*/

#include "Arduino.h"
#include "Input.h"

Input::Input(byte pin, boolean invert)
{
  pinMode(pin,INPUT);
  _pin=pin;
  digitalWrite(pin,HIGH);
  _invert=invert;
  if(digitalRead(pin)==HIGH) _oldstate=__ON;
  else _oldstate=__OFF;
}


void Input::update()
{
  byte i;
  boolean state=false;
  for (i=0; i<25 ;++i)
  {
    if (digitalRead(_pin) == HIGH) state=true;
    delay(2);
  }
  // Update _oldstate with changes.
  if (_oldstate==__OFF && !state) _oldstate=__OFF;
  else if (_oldstate==__ON && state ) _oldstate=__ON;
  else if (_oldstate==__OFF && state) _oldstate=__PRESSED;
  else if(_oldstate==__PRESSED && state) _oldstate=__ON;
  else if(_oldstate==__ON && !state) _oldstate=__RELEASED;
  else if(_oldstate==__RELEASED && !state) _oldstate=__OFF;
  else  _oldstate=__OFF; // must be fault so reset to Off
}


boolean Input::on()
{
  update();
  if( _oldstate==__ON && !_invert) return(true);
  else if (_oldstate==__OFF && _invert) return(true);
  else return(false);
}


boolean Input::off()
{
  update();
  if( _oldstate==__OFF && !_invert) return(true);
  else if (_oldstate==__ON && _invert) return(true);
  else return(false);



and here's the program

Code: [Select]
#include "Relay.h"
#include "Input.h"

Relay Water(9,500,LOW);
Input Modeswitch(10, false);

boolean wait( unsigned long time,  boolean(*test)(void)) 
{
  unsigned long i;
  boolean temp=true;
  for (i=0;i<(time/50);++i)
    if(test())
    {
      temp=false;
      i=time;
    }
  return(temp);
}


void setup()
{
  Water.off();   
}


void loop()
{
  if (wait( 3000, Modeswitch.on ))
  {
    Water.on();
  }
  else Water.off();
  delay(2000);
}


Thanks.

PaulS

Quote
I'm assuming you don't need the header file.

Bad assumption. The header file is actually the most important piece.

jeffvader

Here it is

Code: [Select]
/*
  Input.h
*/
#ifndef Input_h
#define Input_h

#include "Arduino.h"

class Input {
  public:
   Input (byte pin, boolean invert);
   boolean on();
   boolean off();
  private:
   byte _pin;
   byte _oldstate;
   boolean _invert;
   void update();
};

#endif


Thanks

PaulS

I can't get your code to compile. The Relay.h and Relay.cpp files are missing. I commented out the instance and calls, and added a #include "Input.h" to the sketch. Still, I get:
Code: [Select]
Input.cpp: In constructor 'Input::Input(byte, boolean)':
Input.cpp:13: error: '__ON' was not declared in this scope
Input.cpp:14: error: '__OFF' was not declared in this scope
Input.cpp: In member function 'void Input::update()':
Input.cpp:27: error: '__OFF' was not declared in this scope
Input.cpp:28: error: '__ON' was not declared in this scope
Input.cpp:29: error: '__PRESSED' was not declared in this scope
Input.cpp:30: error: '__PRESSED' was not declared in this scope
Input.cpp:31: error: '__RELEASED' was not declared in this scope
Input.cpp:32: error: '__RELEASED' was not declared in this scope
Input.cpp: In member function 'boolean Input::on()':
Input.cpp:39: error: '__ON' was not declared in this scope
Input.cpp:40: error: '__OFF' was not declared in this scope
Input.cpp: In member function 'boolean Input::off()':
Input.cpp:47: error: '__OFF' was not declared in this scope
Input.cpp:48: error: '__ON' was not declared in this scope


What you need to do is make the methods that you want to pass to the wait function static. That means that there is just one instance of the method, not one per instance of the class. This, of course, means that the static methods can not access non-static instance data directly. So, the method needs some way of knowing what instance it is to operate on.

Andy Brown

#6
May 07, 2012, 01:05 pm Last Edit: May 07, 2012, 01:24 pm by Andy Brown Reason: 1
Your function pointer needs to be scoped to the class it's coming from. Adapt your code to this general pattern and it will work.

Code: [Select]

struct foo {
 bool func() {}
};

void bar(bool (foo::*)()) {
}

void usage() {
 bar(&foo::func);
}


Note that you can use plain function pointer syntax if you make the method static.

EDIT: since this area of C++ is one of the more mind-bending in terms of syntactical gymnastics. Here's a more complete example with an actual call being made:

Code: [Select]

struct foo {
  bool func() {}
} myfoo;

typedef bool (foo::*funcptr)();

void bar(foo& objref,funcptr methodptr) {
  (objref.*methodptr)();
}

void usage() {
  bar(myfoo,&foo::func);
}

Home of the Nokia QVGA TFT LCD hacks: http://andybrown.me.uk

jeffvader

Thanks,  not pretending understand but I'll give it a go.

PaulS:  For some reason this was left off the cut-n-paste for Input.cpp.  It should compile with this in it.

Code: [Select]
const byte __ON=100;
const byte __OFF=101;
const byte __PRESSED=102;
const byte __RELEASED=103;

maniacbug

Or you could simply make a global function "bool modeswitch_on() { return Modeswitch.on; }", and pass that into wait.  Probably going to make the code more understandable.

jeffvader

Thanks that seemed to compile.  Yet to test on board.

Andy:  Thanks for your input,  I couldn't get it to compile and thats really down to my lack of understanding of the syntax and your example.

maniacbug

As an aside...  Someday hopefully Arduino will turn on c++11, in which case we can do this:

Code: [Select]

  if (wait( 3000, []{ return Modeswitch.on; } ))
  {
    Water.on();
  }


Which is actually only a few characters away from exactly what you want to do.

Go Up