RyanN
RyanN

Reputation: 747

How to pass a method to a class if you don't know the exact class of the method being passed

I apologise for not knowing how to describe my question. I hope the example will be clear.

//My Arduino based device has a real time clock for which I use a library
#include <RTClib.h> 
RTC_DS3234  RTClock(PIN_CLOCK_CS); // uses SPI
DateTime dt = RTClock.now();
// I also have written a logging class which is a singleton
#include <Logger_SD.h>
Logger_SD::Instance()->initializeSD(PIN_SD_CS,PIN_CLOCK_CS,&dt);

So my logger class now uses a pointer to dt for it's date when generating log messages. Unfortunately, everytime I call the logger, I have to update dt so it has the correct date and time.

dt = RTClock.now();
Logger_SD::Instance()->msgL(INFO,F("Setting Up MPU6050 Gyro."));

I'd like to pass the RTClock.now() method which always returns a DateTime to Logger_SD so it can get it's own damn time. Unfortunately, there are many kinds of RTCs. I could have done:

RTC_DS1307 RTClock(0x68); // Uses i2c
// or
RTC_DS3231 RTClock(0x68); // Also I2c, but does more.
// Lots more.
// then again...
DateTime dt;
dt = RTClock.now();
// I'd like to do something like:
DateTime myNow() return RTClock.now();
Logger_SD::Instance()->initializeSD(PIN_SD_CS,PIN_CLOCK_CS,&myNow);

These all have a now() method that returns a DateTime, but how do I pass the now() method to Logger_SD without having to have a special case for every class of RTC object?

My Logger_SD.h looks a bit like this if it helps:

class Logger_SD {
    public: // I don't quite understand the mechanics here, but it works great.
        static Logger_SD* Instance(); 
        bool initializeSD(const uint8_t, const uint8_t disable_chip_select,DateTime* dt);
        void msgL(LOG_LEVEL logLevel,const __FlashStringHelper *format, ... );
        // ...
    private:
        Logger_SD(){};
        Logger_SD(Logger_SD const&){};
        Logger_SD& operator=(Logger_SD const&){};
        static Logger_SD* m_pInstance;
        DateTime    *_dt;
        // ...
};

I hope this has been clear.

Here's a simple example:

class RTC1 {
  public:
    char  now() { return 'A';}
};
class RTC2 {
  public:
    char  now() { return 'B';}
};
class Logger {
  public:
    void ini(char f()) { logTime = &f ;} // this doesn't work
    char (*logTime)();
};
void setup(){
  Serial.begin(57600);
  RTC1 myRTC1;
  Logger myLogger;
  myLogger.ini(&myRTC1.now);
  Serial.println(MyLogger.logTime()); // Should print 'A'
  // now on a diffent kind of RTC for the same Logger
  RTC2 myRTC2;
  myLogger.ini(&myRTC2.now);
  Serial.println(MyLogger.logTime()); // Should print 'B'
}

Upvotes: 0

Views: 65

Answers (1)

Barry
Barry

Reputation: 302797

myRTC1.now cannot be converted to a function pointer - it's a class method. You need an instance of myRTC1 to call now on. What you're trying to do as-is is impossible.

What you can instead do is take a std::function:

class Logger {
public:
    using LogFunc = std::function<char()>; // any function that takes no 
                                          // args and returns a char

    template <typename F>
    void ini(F&& f) { logTime = std::forward<F>(f); }
    LogFunc logTime;
};

Which you can then assign thusly:

Logger myLogger;
myLogger.ini(std::bind(&RTC1::now, myRTC1));
myLogger.ini([&]{ return myRTC2.now(); });

Upvotes: 1

Related Questions