The State Design pattern in C++ using timer and notification

As a #guru, let me today pay my tributes to my #guru in the tech world who has influenced my technical attributes and mindset.
Although I have never seen them, neither they know I exist.
But I am grateful to them - and because of them I have at last become an engineer in the true sense.
First guru: The authors of The Gang of Four Design Pattern Book
Second Guru: Linus Torvalds for offering Linux and rather helping me come out of the vicious cycle of windows
Third Guru: The Android creator and Google for showing me the different nitty-gritty of a complicated framework that i learnt through delving into the Android framework code
Fourth Guru: A professor of USA University by the name Doug from whom i got insights of how to study properly the Android OS difficult parts of the code like Asynctask Framework and he showed me the right approach of taking the OS study from en engineer’s perspective - delving into difficult concurrent framework of the Android OS
And my all-time Guru : My family for giving me a sweet, caring, loving #home and my only son, #ridit, for becoming my best student from his early childhood. He is still driving me to burn the midnight oil in front of the laptop to explore the deep ocean of Computer science.
And I am sure, the way the whole professional world of technology is moving, very soon somebody will have to dismiss the book called
#theworldisflat - The World Is Flat
and write a new one
#theflatistheworld - The Flat Is The World
Now let's come back to the subject. Let's discuss the technical side of the State Design Pattern.
The state design pattern is a powerful tool in software design, allowing you to encapsulate the behavior of an object based on its internal state.
The main participants in this design pattern are:
Context : This object represents the entire entity experiencing state changes. It holds a reference to the current state object and exposes an interface for external interaction.
State : These are concrete classes representing each possible state the object can be in. Each state class defines its own behavior for responding to events and transitions.
Transitions : These define how the object can move from one state to another. They can be triggered by events, user actions, or internal conditions.
In my example, I have cited the process of cooking chicken - how the chicken states alter from uncleaned state to cleaned state to marinated state to cooked state and finally served to the guests.
The source code is as follows:
/*
* Callback.h
* Created on: May 5, 2022
* Author: som
*/
#ifndef CALLBACK_H_
#define CALLBACK_H_
class Callback {
public:
virtual void goToNextStateFromCallback() = 0;
virtual ~Callback() {}
};
#endif /* CALLBACK_H_ */
Here's the formatted version of the code:
#ifndef CHICKENSTATE_H_
#define CHICKENSTATE_H_
#include <iostream>
#include <cstring>
#include "Mamma.h"
#include "timer.h"
using namespace std;
class Mamma;
class ChickenState {
public:
ChickenState() {}
virtual ~ChickenState() {
cout << "aab chicken khatam... sablog khaa liya" << endl;
}
virtual void wash(int periodForDisplayMessage, int theTotalTimeNeeded) = 0;
virtual void marinate(int periodForDisplayMessage, int theTotalTimeNeeded) = 0;
virtual void cook(int periodForDisplayMessage, int theTotalTimeNeeded) = 0;
virtual void serve(int periodForDisplayMessage, int theTotalTimeNeeded) = 0;
virtual void setMamma() = 0;
virtual void goToNextState() = 0;
virtual char* getTag() = 0;
};
#endif
#ifndef UNCLEANEDSTATE_H_
#define UNCLEANEDSTATE_H_
#include "Mamma.h"
#include "CleanedState.h"
#include "Callback.h"
class UncleanedState : public ChickenState /*, public Callback*/ {
private:
Mamma* mamma;
char* tag;
public:
UncleanedState(Mamma* inMamma) {
tag = new char[40];
mamma = inMamma;
strcpy(tag, "Uncleaned");
}
// Override
void setMamma() {
// mamma = ChickenState::getMamma();
}
virtual ~UncleanedState() {
delete[] tag;
}
void wash(int periodForDisplayMessage, int theTotalTimeNeeded) {
cout << "The cooking process is starting... enjoy..." << endl;
cout << "chicken is getting washed.... please wait..." << endl;
/*
(mamma->t).setInterval(& {
cout << "chicken is getting washed.... please wait..." << endl;
}, periodForDisplayMessage);
*/
(mamma->t).setTimeout(& {
goToNextState();
}, theTotalTimeNeeded);
}
void marinate(int periodForDisplayMessage, int theTotalTimeNeeded) {}
void cook(int periodForDisplayMessage, int theTotalTimeNeeded) {}
void serve(int periodForDisplayMessage, int theTotalTimeNeeded) {}
// Override
void goToNextState() {
mamma->isUncleanedStateDone = true;
// will callback the methods defined in the Mamma class...
(mamma->t).stop();
}
// Override
char* getTag() {
return tag;
}
};
#endif
#ifndef CLEANEDSTATE_H_
#define CLEANEDSTATE_H_
#include "Mamma.h"
#include "MarinatedState.h"
//#include "Callback.h"
class CleanedState : public ChickenState /*, public Callback*/ {
private:
Mamma* mamma;
char* tag;
public:
CleanedState(Mamma* inMamma) {
tag = new char[40];
mamma = inMamma;
strcpy(tag, "Cleaned");
}
virtual ~CleanedState() {
delete[] tag;
}
// Override
void setMamma() {
// mamma = ChickenState::getMamma();
}
void wash(int periodForDisplayMessage, int theTotalTimeNeeded) {
}
void marinate(int periodForDisplayMessage, int theTotalTimeNeeded) {
cout << "chicken is getting marinated.... please wait..." << endl;
// mamma->timerRunning = true;
/*
(mamma->t).setInterval(& {
cout << "chicken is getting marinated.... please wait..." << endl;
}, periodForDisplayMessage);
*/
(mamma->t).setTimeout(& {
goToNextState();
}, theTotalTimeNeeded);
}
void cook(int periodForDisplayMessage, int theTotalTimeNeeded) {
}
void serve(int periodForDisplayMessage, int theTotalTimeNeeded) {
}
void goToNextState() {
mamma->isCleanedStateDone = true;
(mamma->t).stop();
}
// Override
char* getTag() {
return tag;
}
};
#endif
#ifndef MARINATEDSTATE_H_
#define MARINATEDSTATE_H_
#include "ChickenState.h"
#include "CookedState.h"
//#include "Callback.h"
class MarinatedState : public ChickenState /* , public Callback */ {
private:
Mamma* mamma;
char* tag;
public:
MarinatedState(Mamma* inMamma) {
tag = new char[40];
mamma = inMamma;
strcpy(tag, "Marinated");
}
virtual ~MarinatedState() {
delete[] tag;
}
// Override
void setMamma() {
// mamma = ChickenState::getMamma();
}
void wash(int periodForDisplayMessage, int theTotalTimeNeeded) {
// Implementation here
}
void marinate(int periodForDisplayMessage, int theTotalTimeNeeded) {
// Implementation here
}
void cook(int periodForDisplayMessage, int theTotalTimeNeeded) {
cout << "chicken is getting cooked... please wait..." << endl;
/*
(mamma->t).setInterval(& {
cout << "chicken is getting cooked... please wait..." << endl;
}, periodForDisplayMessage);
*/
(mamma->t).setTimeout(& {
goToNextState();
}, theTotalTimeNeeded);
}
void serve(int periodForDisplayMessage, int theTotalTimeNeeded) {
// Implementation here
}
void goToNextState() {
mamma->isMarionatedStatedone = true;
(mamma->t).stop();
}
// Override
char* getTag() {
return tag;
}
};
#endif
#ifndef COOKEDSTATE_H_
#define COOKEDSTATE_H_
#include "Mamma.h"
class CookedState : public ChickenState /*, public Callback*/ {
private:
Mamma* mamma;
char* tag;
public:
// Timer t;
CookedState(Mamma* inMamma) {
tag = new char[40];
mamma = inMamma;
strcpy(tag, "Cooked");
}
virtual ~CookedState() {
delete[] tag;
}
// Override
void setMamma() {
// mamma = ChickenState::getMamma();
}
void wash(int periodForDisplayMessage, int theTotalTimeNeeded) {}
void marinate(int periodForDisplayMessage, int theTotalTimeNeeded) {}
void cook(int periodForDisplayMessage, int theTotalTimeNeeded) {}
void serve(int periodForDisplayMessage, int theTotalTimeNeeded) {
cout << "chicken is ready.... enjoy the food..." << endl;
cout << "this was the last state..." << endl;
cout << "i hope you liked the delicious aajwayni chicken..." << endl;
mamma->isCookedStateDone = true;
}
// Override
void goToNextState() {}
// Override
char* getTag() {
return tag;
}
};
#endif
/*
* Mamma.cpp
* Created on: May 5, 2022
* Author: som
*/
#include "Mamma.h"
#include "UncleanedState.h"
#include "timer.h"
#include <iostream>
#include <cstring>
Mamma::Mamma() {
isUncleanedStateDone = false;
isCleanedStateDone = false;
isMarionatedStatedone = false;
isCookedStateDone = false;
chicken = new UncleanedState(this);
currentState = chicken;
t.setCallback(this);
timerRunning = true;
}
Mamma::~Mamma() {
delete chicken;
delete currentState;
std::cout << "Mamma - the cook is leaving the stage" << std::endl;
}
ChickenState* Mamma::getChicken() {
return chicken;
}
void Mamma::changeChickenState(ChickenState* toChickenState) {
chicken = toChickenState;
currentState = chicken;
}
void Mamma::startPreparation(bool timerRunning) {
if (!isUncleanedStateDone) {
chicken->wash(1000, 3000);
}
// Next state is CleanedState
if (isUncleanedStateDone) {
chicken->marinate(1000, 3000);
}
// Next state is Marinated State
if (isCleanedStateDone) {
chicken->cook(200, 5000);
}
// Next state is cookedState... this will be the last state
if (isMarionatedStatedone) {
chicken->serve(200, 2000);
}
while (timerRunning);
}
void Mamma::goToNextStateFromCallback() {
timerRunning = false;
if (strstr(currentState->getTag(), "Uncleaned")) {
ChickenState* nextState = new CleanedState(this);
changeChickenState(nextState);
// Just stop the timer thread...
t.justStop();
timerRunning = true;
// Recursion...
startPreparation(timerRunning);
}
if (strstr(currentState->getTag(), "Cleaned")) {
ChickenState* nextState = new MarinatedState(this);
changeChickenState(nextState);
// Just stop the timer...
t.justStop();
timerRunning = true;
// Recursion...
startPreparation(timerRunning);
}
if (strstr(currentState->getTag(), "Marinated")) {
ChickenState* nextState = new CookedState(this);
changeChickenState(nextState);
t.justStop();
timerRunning = true;
// Recursion...
startPreparation(timerRunning);
}
if (strstr(currentState->getTag(), "Cooked")) {
t.justStop();
timerRunning = false;
startPreparation(timerRunning);
}
}
Here's the formatted version of the code:
#ifndef TIMER_H_
#define TIMER_H_
#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>
#include "Callback.h"
using namespace std;
class Timer {
private:
Callback* cb;
public:
Timer() {}
void setCallback(Callback* cb) {
this->cb = cb;
}
atomic_bool active = { true };
void setTimeout(auto function, int delay) {
active = true;
thread t([=] {
if (!active.load()) return;
this_thread::sleep_for(chrono::milliseconds(delay));
if (!active.load()) return;
function();
});
t.join();
}
void setInterval(auto function, int interval) {
active = true;
thread t([=] {
while (active.load()) {
this_thread::sleep_for(chrono::milliseconds(interval));
if (!active.load()) return;
function();
}
});
t.detach();
}
void stop() {
cb->goToNextStateFromCallback();
}
void justStop() {
active = false;
}
void start() {
active = true;
}
};
#endif
#include <iostream>
#include <memory>
#include "Mamma.h"
using namespace std;
int main() {
unique_ptr<Mamma> mamma = make_unique<Mamma>();
mamma->startPreparation(true);
return 0;
}
The Copiler and linker option for the above program is
The video of the output if we run the above program will be as follows:
Here is the complete source code for the developers...
https://gitlab.com/som.mukhopadhyay/state-pattern-in-c-with-timer
Subscribe to my newsletter
Read articles from Somenath Mukhopadhyay directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Somenath Mukhopadhyay
Somenath Mukhopadhyay
To win is no more than this... To rise each time you fall...