samedi 30 janvier 2021

How do I call c++ function pointers created in one class and then passed to another?

I've sort of programmed myself into a corner and in the process, realized that I am really lacking in my understanding of c++ function pointers as well as virtual functions. The goal is to implement a temperature controller on a raspberry pi. I won't go into detail on the other components as they are not relevant. I am currently working on the button controller. This is more of an architecture question.

  • TempCtrl is the main class that calls all others. It also calls the ButtonController::addButton() to register buttons, and contains the callbacks.
  • ButtonCtrl reads the gpio registers and cycles through the registered buttons stored in a vector of type Button.
  • Button class was supposed to be a very simple container which had nothing other than an integer specifying the gpio, and callbacks. This is where the problem is.

I've never really done anything like this before, but I wanted something really clean. Basically, once I finish the button controller I wanted to be able to leave it alone unless I was adding functionality. For this reason, I wanted callback functions to be defined in the TempCtrl Function.

So far this has not worked. I'm sure my syntax is off if it is even possible. I would also be interested in alternative ways of achieving this.

Code below. I only posted the relevant headers, and the buttoncontroller.cpp as there is quite a lot of code. I will be glad to post more code if more information is needed, so please just ask:

button.hpp

#ifndef BUTTON_HPP
#define BUTTON_HPP

#include <ctime>
#include <cstdint>

#define HOLDTIME    500  // milliseconds

class TempCtrl;

enum State_E
{
  BP_LOW, 
  BP_HIGH
};

class Button
{
  public:
  Button(
    uint8_t _gpio, 
    void(TempCtrl::*)(), 
    void(TempCtrl::*)(), 
    void(TempCtrl::*)());

  ~Button();

  void update(State_E newValue);

  uint8_t getGpio() const;

  private:
  void (TempCtrl::*onFallingEdge)();

  void (TempCtrl::*onLongHold)();

  void (TempCtrl::*onRisingEdge)();
  
  uint8_t gpio;

  State_E state;

  time_t timeStamp;
};

#endif /* BUTTON_HPP */

buttonprocessor.hpp

#ifndef BUTTONPROCESSOR_HPP
#define BUTTONPROCESSOR_HPP

#include "button.hpp"
#include <cstdint>
#include <vector>

#define GPIO_MAX    53
#define BLOCK_SIZE  (4*1024)
#define GPLEV0      13
#define GPLEV1      14
#define SCAN_RATE   100  // milliseconds

class ButtonProcessor
{
  public:
  ButtonProcessor();

  virtual ~ButtonProcessor();

  bool init();

  bool shutdown();
  
  size_t addButton(Button button);

  void startEventLoop(bool &terminate);

  private:

  int fd; 

  volatile uint32_t *gpio_base;

  std::vector<Button> buttonDb;
};

#endif /* BUTTONPROCESSOR_HPP */

tempctrl.hpp

#ifndef TEMPCTRL_HPP
#define TEMPCTRL_HPP
#include "button.hpp"
#include "lcdscreen.hpp"
#include "buttonprocessor.hpp"

#include <cstdlib>
#include <vector>
#include <string>
#include <time.h>
#include <cstring>
#include <memory>

#define W1_DEV_ROOT "/sys/bus/w1/devices/w1_bus_master1/"
#define W1_SLAVE_COUNT W1_DEV_ROOT "w1_master_slave_count"
#define W1_SLAVES W1_DEV_ROOT "w1_master_slaves"
#define POLL_FREQ 1000

/***********************************************
*  A structure for holding a single data point *
*  of temperature in degrees celsius, as well  *
*  as the temp probe ID and a time stamp.      *
************************************************/

struct TempStruct
{
  time_t      ts;
  std::string id;
  float       temp;
};

enum TScale_E
{
  CELSIUS,
  FARENHEIT,
  KELVIN,
  RANKINE,
  MAX_VALUE_TSCALE
};

#define DEFAULT_TEMP_SCALE FARENHEIT

enum InputMode_E
{
  SETPOINT,
  LOAD_PROFILE,
  PID_TUNE,
  MAX_VALUE_INPUT_MODE
};

class TempCtrl 
{   
public:
  TempCtrl();
    
  virtual ~TempCtrl();
    
  /***********************************************
  *  Fetches the current time from the specified *
  *  temperature controller, and stores the      *
  *  returned value with time stamp and device   *
  *  id, in the tempStruct array.    
  ************************************************/
  void getTemp(int);

  void setTempScale(TScale_E);

  void lcdToggleEnable(int bits);

  void wait(size_t seconds);

  void printTemp(enum TScale_E);

  std::vector<std::string> slavesArr;

  std::vector<TempStruct> tempStruct;

  TScale_E tempScaleVal = CELSIUS;

  int fd;

// These should not be public 
  void onFallingEdge();

  void onLongHold();

  void onRisingEdge();

private:
  void lcdInit();

  uint8_t tempInit(void);

  /***********************************************
  * exclusive access to atomic safe lcdscreen    *
  * manipulation.  Logic is completely decoupled *
  ************************************************/
  std::unique_ptr<LcdScreen> mLcdScreen;

  /***********************************************
  * The responsibility of the button processor   *
  * is to detect edge changes in for specified   *
  * GPIO's, determine the type of press and call *
  * the relavant callback.                       * 
  ************************************************/
  std::unique_ptr<ButtonProcessor> mButtonProcessor;

};
#endif /* TEMPCTRL_HPP */

buttonprocessor.cpp

#include "button.hpp"

Button::Button(
    uint8_t _gpio,
    void(TempCtrl::*_onFallingEdge)(),
    void(TempCtrl::*_onLongHold)(),
    void(TempCtrl::*_onRisingEdge)())
: state(BP_LOW),
timeStamp(time(NULL)),
onFallingEdge(_onFallingEdge),
onLongHold(_onLongHold),
onRisingEdge(_onRisingEdge)
{
  
}
  
Button::~Button()
{ 
  
}

void Button::update(State_E newValue)
{   
  if(
    newValue != state &&
    state == BP_HIGH) // Rising edge
  { 
    timeStamp = time(NULL);
    state = newValue;
    // ((Button*)this)->Button::onRisingEdge(); 
  }
  else if(
    state == BP_HIGH && 
    newValue == state &&
    ((time(NULL) - timeStamp) > HOLDTIME)) // Long Hold
  {
    timeStamp = time(NULL);
    state = newValue; 
    // ((Button*)this)->Button::onLongHold(); 
  }
  else if(
    newValue != state && 
    state == BP_LOW) // Falling edge
  {      
    timeStamp = time(NULL);
    state = newValue;
    TempCtrl.(*((Button*)this)->Button::onFallingEdge());
    //onFallingEdge();
  }  
}   
    
uint8_t Button::getGpio() const
{   
  return gpio; 
}  

Aucun commentaire:

Enregistrer un commentaire