Class-declaration inside the *.ino-file. Can it be made a forward declaration?

Hi everybody,

today I wrote my first own class.
If the class-declaration is above the instantiation the code compiles


class MonoFlop {
    byte triggerInputPin;
    byte PinToSwitch;

    unsigned long InitialWaitTime = 1000; // set to zero if you don't want an initial wait
    unsigned long ActiveTime = 5000;
    unsigned long LockedTime = 3000;      // set to zero if you don't want a lock-time

    byte triggerIsActive = LOW; // LOW o HIGH
    byte OutPutOn  = HIGH;
    // the attention-mark ! is the not-operator which inverts the value
    // !true = false  !false = true     !HIGH = LOW    !LOW = HIGH
    byte OutPutOff = !OutPutOn;

    // set InitialWaitTime to 0 if you don't want a delay between triggering and switching output

    unsigned long MonoFlopTimer; // variable used for non-blocking timing

    // constants of the state-machine
    #define sm_Idling      0
    #define sm_InitialWait 1
    #define sm_Activated   2
    #define sm_Locked      3
    #define sm_stopped     4

    byte MonoFlopState = sm_Idling; // state-variable of the state-machine

    // constructor
  public:
    MonoFlop(byte cp_triggerPin, byte cp_outputPin) {
      triggerInputPin = cp_triggerPin;
      PinToSwitch     = cp_outputPin;
      MonoFlopState   = sm_stopped;
      pinMode(triggerInputPin, INPUT_PULLUP);
      pinMode(PinToSwitch,     OUTPUT);
      digitalWrite(PinToSwitch, OutPutOff);
    }

    void deactivate() {
      digitalWrite(PinToSwitch, OutPutOff);
      MonoFlopState   = sm_stopped;;
    }

    void activate() {
      MonoFlopState   = sm_Idling;
    }

    void run() {
      MonoFlopStateMachine();
    }

    void setOutputDelay(unsigned long p_outputDelay) {
      InitialWaitTime = p_outputDelay;
    }

    void setHighTime(unsigned long p_HighTime) {
      ActiveTime = p_HighTime;
    }

    void setLockTime(unsigned long p_LockTime) {
      LockedTime = p_LockTime;
    }

    void setTriggerLevel(byte p_TriggerLevel) {
      triggerIsActive = p_TriggerLevel;
    }

    void setOutputOnLevel(byte p_OnLevel) {
      OutPutOn = p_OnLevel;
      OutPutOff = !OutPutOn;
    }

    boolean TPiO(unsigned long &startOfPeriod, unsigned long TimePeriod) {
      unsigned long currentMillis  = millis();
      if ( currentMillis - startOfPeriod >= TimePeriod ) {
        // more time than TimePeriod has elapsed since last time if-condition was true
        startOfPeriod = currentMillis; // a new period starts right here so set new starttime
        return true;
      }
      else return false;            // actual TimePeriod is NOT yet over
    }

    void MonoFlopStateMachine() {

      switch (MonoFlopState) {
        case sm_stopped:
          // do absolutely nothing
          break;

        case sm_Idling:
          // check if trigger-input reads triggering signal
          if (digitalRead(triggerInputPin) == triggerIsActive) {
            MonoFlopTimer = millis();       // make snapshot of time
            MonoFlopState = sm_InitialWait; // set next state of state-machine
            Serial.println( F("trigger-signal detected start inital wait") );
          }
          break; // IMMIDIATELY jump down to END OF SWITCH

        case sm_InitialWait:
          // wait until initial waittime has passed by
          if ( TPiO(MonoFlopTimer, InitialWaitTime) ) {
            // if waittime HAS passed by
            MonoFlopTimer = millis();           // make new snapshot of time
            digitalWrite(PinToSwitch, OutPutOn); // switch IO-Pin to activated state
            MonoFlopState = sm_Activated;       // set next state of state-machine
            Serial.println( F("InitialWaitTime is over switching output to ACTIVE") );
          }
          break; // IMMIDIATELY jump down to END OF SWITCH

        case sm_Activated:
          // check if time the IO-pin shall be active is over
          if ( TPiO(MonoFlopTimer, ActiveTime) ) {
            // if activetime of IO-pin IS over
            MonoFlopTimer = millis();             // make new snapshot of time
            digitalWrite(PinToSwitch, OutPutOff); // switch IO-pin to DE-activated state
            MonoFlopState = sm_Locked;            // set next state of state-machine
            Serial.println( F("Activetime is over switching output to IN-active") );
            Serial.println( F("and starting locktime") );
          }
          break; // IMMIDIATELY jump down to END OF SWITCH

        case sm_Locked:
          // check if time the logic shall be locked against too early re-triggering is over
          if ( TPiO(MonoFlopTimer, LockedTime) ) {
            // if locked time IS over
            Serial.println( F("locktime is over change state to idling") );
            MonoFlopState = sm_Idling; // set state-machine to idle-mode
          }
          break; // IMMIDIATELY jump down to END OF SWITCH
      } // END OF SWITCH
    }

}; // end of class MonoFlop

const byte myTriggerPin = 2;
const byte myOutputPin  = 3;


MonoFlop myMonoflop1(myTriggerPin, myOutputPin);


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;  // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();
}



void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);
  myMonoflop1.run();
}

For readabilty reasons I want to put the class-declartion at the bottom of the code
If I simply move the class-declaration to the bottom of the *.ino-file I get an
MonoFlop-006-Demo:54:3: error: 'myMonoflop1' was not declared in this scope
error.

Sure if the compiler knows nothing about the class the compiler complains.

So I tried to use a forward-declaration like with functions

class MonoFlop(byte cp_triggerPin, byte cp_outputPin);
 
const byte myTriggerPin = 2;
const byte myOutputPin  = 3;

MonoFlop myMonoflop1(myTriggerPin, myOutputPin);

but then I get an compiler-error

MonoFlop-006-Demo:2:21: error: expected ')' before 'cp_triggerPin'
 class MonoFlop(byte cp_triggerPin, byte cp_outputPin);

I have googled some time if I could find something about forward-declaration of a class but did'nt find anything useful.

So my question is: is it possible to have a forward-declartion of a class?
And if yes how does it look like?
complete code that does not compile


class MonoFlop(byte cp_triggerPin, byte cp_outputPin);
 
const byte myTriggerPin = 2;
const byte myOutputPin  = 3;

MonoFlop myMonoflop1(myTriggerPin, myOutputPin);


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;  // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);
  myMonoflop1.run();
}


class MonoFlop {
    byte triggerInputPin;
    byte PinToSwitch;

    unsigned long InitialWaitTime = 1000; // set to zero if you don't want an initial wait
    unsigned long ActiveTime = 5000;
    unsigned long LockedTime = 3000;      // set to zero if you don't want a lock-time

    byte triggerIsActive = LOW; // LOW o HIGH
    byte OutPutOn  = HIGH;
    // the attention-mark ! is the not-operator which inverts the value
    // !true = false  !false = true     !HIGH = LOW    !LOW = HIGH
    byte OutPutOff = !OutPutOn;

    // set InitialWaitTime to 0 if you don't want a delay between triggering and switching output

    unsigned long MonoFlopTimer; // variable used for non-blocking timing

    // constants of the state-machine
    #define sm_Idling      0
    #define sm_InitialWait 1
    #define sm_Activated   2
    #define sm_Locked      3
    #define sm_stopped     4

    byte MonoFlopState = sm_Idling; // state-variable of the state-machine

    // constructor
  public:
    MonoFlop(byte cp_triggerPin, byte cp_outputPin) {
      triggerInputPin = cp_triggerPin;
      PinToSwitch     = cp_outputPin;
      MonoFlopState   = sm_stopped;
      pinMode(triggerInputPin, INPUT_PULLUP);
      pinMode(PinToSwitch,     OUTPUT);
      digitalWrite(PinToSwitch, OutPutOff);
    }

    void deactivate() {
      digitalWrite(PinToSwitch, OutPutOff);
      MonoFlopState   = sm_stopped;;
    }

    void activate() {
      MonoFlopState   = sm_Idling;
    }

    void run() {
      MonoFlopStateMachine();
    }

    void setOutputDelay(unsigned long p_outputDelay) {
      InitialWaitTime = p_outputDelay;
    }

    void setHighTime(unsigned long p_HighTime) {
      ActiveTime = p_HighTime;
    }

    void setLockTime(unsigned long p_LockTime) {
      LockedTime = p_LockTime;
    }

    void setTriggerLevel(byte p_TriggerLevel) {
      triggerIsActive = p_TriggerLevel;
    }

    void setOutputOnLevel(byte p_OnLevel) {
      OutPutOn = p_OnLevel;
      OutPutOff = !OutPutOn;
    }

    boolean TPiO(unsigned long &startOfPeriod, unsigned long TimePeriod) {
      unsigned long currentMillis  = millis();
      if ( currentMillis - startOfPeriod >= TimePeriod ) {
        // more time than TimePeriod has elapsed since last time if-condition was true
        startOfPeriod = currentMillis; // a new period starts right here so set new starttime
        return true;
      }
      else return false;            // actual TimePeriod is NOT yet over
    }

    void MonoFlopStateMachine() {

      switch (MonoFlopState) {
        case sm_stopped:
          // do absolutely nothing
          break;

        case sm_Idling:
          // check if trigger-input reads triggering signal
          if (digitalRead(triggerInputPin) == triggerIsActive) {
            MonoFlopTimer = millis();       // make snapshot of time
            MonoFlopState = sm_InitialWait; // set next state of state-machine
            Serial.println( F("trigger-signal detected start inital wait") );
          }
          break; // IMMIDIATELY jump down to END OF SWITCH

        case sm_InitialWait:
          // wait until initial waittime has passed by
          if ( TPiO(MonoFlopTimer, InitialWaitTime) ) {
            // if waittime HAS passed by
            MonoFlopTimer = millis();           // make new snapshot of time
            digitalWrite(PinToSwitch, OutPutOn); // switch IO-Pin to activated state
            MonoFlopState = sm_Activated;       // set next state of state-machine
            Serial.println( F("InitialWaitTime is over switching output to ACTIVE") );
          }
          break; // IMMIDIATELY jump down to END OF SWITCH

        case sm_Activated:
          // check if time the IO-pin shall be active is over
          if ( TPiO(MonoFlopTimer, ActiveTime) ) {
            // if activetime of IO-pin IS over
            MonoFlopTimer = millis();             // make new snapshot of time
            digitalWrite(PinToSwitch, OutPutOff); // switch IO-pin to DE-activated state
            MonoFlopState = sm_Locked;            // set next state of state-machine
            Serial.println( F("Activetime is over switching output to IN-active") );
            Serial.println( F("and starting locktime") );
          }
          break; // IMMIDIATELY jump down to END OF SWITCH

        case sm_Locked:
          // check if time the logic shall be locked against too early re-triggering is over
          if ( TPiO(MonoFlopTimer, LockedTime) ) {
            // if locked time IS over
            Serial.println( F("locktime is over change state to idling") );
            MonoFlopState = sm_Idling; // set state-machine to idle-mode
          }
          break; // IMMIDIATELY jump down to END OF SWITCH
      } // END OF SWITCH
    }

}; // end of class MonoFlop

best regards Stefan

If it is only for readability I suggest putting a class in its own .hpp file which you then include from your .ino file:

#include "yourClass.hpp"

void setup () ....

1 Like

You can forward-declare something to be a class and then define variables that are pointers and references to that class. But that won't help with what you're trying to do.

@Delta_G's suggestion is the best. Separating the declarations from the implementations has another benefit. If you write the declarations (.h file) using appropriate/logical variable and function names (along with adding helpful comments), then it's much easier for a user of the class to scan through the .h file and figure out how to use it without getting bogged down in the messy details of the implementation (.cpp file). See My Post #5 in this Thread.

2 Likes

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