I have wrote code for a stopwatch that utilizes the abstract design pattern and would like to get some feedback on the code, so be as harsh as you can.
Note: I used ctime
instead of chrono
because this is not meant for benchmarking. The code will later be used in a console game, and the format of the std::tm struct
is easy to work with.
Also, note that the way I wrote the tests in source.cpp will result in a small visual bug from time to time to resolve it lower the waiting time inside of the do while loop from std::chrono::seconds(1)
to std::chrono::milliseconds(250);
and increase the value of timer, the following action will increase the refresh rate.
EDIT: Note that this question is a followup to Watch that uses the abstract factory design pattern they both use the same Console, Constants and Digits library but both accomplish a different task
StopWatch.h:
#ifndef STOP_WATCH
#define STOP_WATCH
#include"Digits.h"
#include<vector>
#include<memory>
#include<ctime>
class StopWatch
{
public:
virtual void printTime() = 0;
void setStopWatchXY(int x, int y);
bool countDownFrom(int seconds);
void updateTime();
void reset();
void start();
void stop();
void lap();
const std::vector<int>& getLapTimes() const;
int getElapsed() const;
virtual ~StopWatch() = default;
protected:
int m_watchXPos;
int m_watchYPos;
int m_seconds;
int m_minutes;
int m_hours;
private:
std::vector<int> m_lapTimes;
bool m_running{ false };
int m_elapsed{};
int m_beg;
std::time_t m_now;
void converter(int seconds);
void clearTime();
};
class DigitalStopWatch final : public StopWatch
{
public:
virtual void printTime() override;
explicit DigitalStopWatch(int x, int y)
{
setStopWatchXY(x, y);
}
};
class SegmentedStopWatch final : public StopWatch
{
public:
virtual void printTime() override;
explicit SegmentedStopWatch(int x, int y)
{
setStopWatchXY(x, y);
}
private:
Digit m_stopWatchDigits[6];
void printDigitAtLoc(Digit digArr[], int index, int x, int y) const;
void printColon(int x, int y);
void printSeconds();
void printMinutes();
void printHours();
void set(Digit digArr[], int startIndex, int unit);
void setDigitsToCurrentTime();
void setSeconds();
void setMinutes();
void setHours();
};
class Factory
{
public:
virtual std::unique_ptr<StopWatch> createStopWatch(int stopWatchXPos = 0, int stopWatchYPos = 0) const = 0;
};
class DigitalStopWatchFactory final : public Factory
{
virtual std::unique_ptr<StopWatch> createStopWatch(int stopWatchXPos = 0, int stopWatchYPos = 0) const override
{
return std::make_unique<DigitalStopWatch>(stopWatchXPos, stopWatchYPos);
}
};
class SegmentedStopWatchFactory final : public Factory
{
virtual std::unique_ptr<StopWatch> createStopWatch(int stopWatchXPos = 0, int stopWatchYPos = 0) const override
{
return std::make_unique<SegmentedStopWatch>(stopWatchXPos, stopWatchYPos);
}
};
#endif
StopWatch.cpp:
#include"StopWatch.h"
#include"Console.h"
#include<thread> //for this_thread::sleep_for
namespace
{
constexpr int maxTime { 356400 };
constexpr int digitPadding { 5 };
constexpr int secondsIndexStart{ 5 };
constexpr int minutesIndexStart{ 3 };
constexpr int timePadding { 2 };
constexpr int hoursIndexStart { 1 };
enum
{
First,
Second,
Third,
Fourth,
Fifth,
Sixth
};
}
/*|---STOP_WATCH_FUNCTIONS_START---|*/
/*|---PUBLIC_FUNCTIONS_START---|*/
void StopWatch::setStopWatchXY(int x, int y)
{
Console::setXY(x, y, m_watchXPos, m_watchYPos);
}
bool StopWatch::countDownFrom(int seconds)
{
if (seconds > maxTime) seconds = maxTime;
while (seconds >= 0)
{
converter(seconds);
printTime();
if (seconds > 0)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}//end of if
--seconds;
}//end of while
return true;
}
void StopWatch::updateTime()
{
long long curTimeInSec{ static_cast<long long>(std::time(&m_now)) - m_beg + m_elapsed };
if (curTimeInSec > maxTime) curTimeInSec = 0;
converter(curTimeInSec);
}
void StopWatch::reset()
{
m_running = false;
m_lapTimes.clear();
m_lapTimes.shrink_to_fit();
clearTime();
}
void StopWatch::start()
{
if (!m_running)
{
m_beg = static_cast<long long>(std::time(&m_now));
m_running = true;
}//end of if
}
void StopWatch::stop()
{
if (m_running)
{
m_elapsed += static_cast<long long>(std::time(&m_now)) - m_beg;
m_running = false;
}//end of if
}
void StopWatch::lap()
{
if (m_running)
{
stop();
m_lapTimes.emplace_back(m_elapsed);
clearTime();
start();
}//end of if
}
const std::vector<int>& StopWatch::getLapTimes() const
{
return m_lapTimes;
}
int StopWatch::getElapsed() const
{
return m_elapsed;
}
/*|----PUBLIC_FUNCTIONS_END----|*/
/*|---PRIVATE_FUNCTIONS_START---|*/
void StopWatch::converter(int seconds)
{
m_hours = seconds / 3600;
seconds = seconds % 3600;
m_minutes = seconds / 60;
m_seconds = seconds % 60;
}
void StopWatch::clearTime()
{
m_elapsed = 0;
m_seconds = 0;
m_minutes = 0;
m_hours = 0;
}
/*|----PRIVATE_FUNCTIONS_END----|*/
/*|----STOP_WATCH_FUNCTIONS_END----|*/
/*|---DIGITAL_STOP_WATCH_FUNCTIONS_START---|*/
/*|---PUBLIC_FUNCTIONS_START---|*/
/*|---VIRTUAL_FUNCTIONS_START---|*/
void DigitalStopWatch::printTime()
{
Console::gotoxy(m_watchXPos, m_watchYPos);
if (m_hours < 10) std::cout << '0';
std::cout << m_hours << ':';
if (m_minutes < 10) std::cout << '0';
std::cout << m_minutes << ':';
if (m_seconds < 10) std::cout << '0';
std::cout << m_seconds;
}
/*|----VIRTUAL_FUNCTIONS_END----|*/
/*|----PUBLIC_FUNCTIONS_END----|*/
/*|----DIGITAL_STOP_WATCH_FUNCTIONS_END----|*/
/*|---SEGMENTED_STOP_WATCH_FUNCTIONS_START---|*/
/*|---PUBLIC_FUNCTIONS_START---|*/
/*|---VIRTUAL_FUNCTIONS_START---|*/
void SegmentedStopWatch::printTime()
{
setDigitsToCurrentTime();
printHours();
printColon(m_watchXPos + 10, m_watchYPos);
printMinutes();
printColon(m_watchXPos + 22, m_watchYPos);
printSeconds();
}
/*|----VIRTUAL_FUNCTIONS_END----|*/
/*|----PUBLIC_FUNCTIONS_END----|*/
/*|---PRIVATE_FUNCTIONS_START---|*/
void SegmentedStopWatch::printDigitAtLoc(Digit digArr[], int index, int x, int y) const
{
digArr[index].setDigitXY(x + index * digitPadding, y);
digArr[index].printDigit();
}
void SegmentedStopWatch::printColon(int x, int y)
{
Console::putSymbol(x, y + 1, '.');
Console::putSymbol(x, y + 2, '.');
}
void SegmentedStopWatch::printSeconds()
{
printDigitAtLoc(m_stopWatchDigits, Fifth, m_watchXPos + timePadding * 2, m_watchYPos);
printDigitAtLoc(m_stopWatchDigits, Sixth, m_watchXPos + timePadding * 2, m_watchYPos);
}
void SegmentedStopWatch::printMinutes()
{
printDigitAtLoc(m_stopWatchDigits, Third, m_watchXPos + timePadding, m_watchYPos);
printDigitAtLoc(m_stopWatchDigits, Fourth, m_watchXPos + timePadding, m_watchYPos);
}
void SegmentedStopWatch::printHours()
{
printDigitAtLoc(m_stopWatchDigits, First, m_watchXPos, m_watchYPos);
printDigitAtLoc(m_stopWatchDigits, Second, m_watchXPos, m_watchYPos);
}
void SegmentedStopWatch::set(Digit digArr[], int startIndex, int unit)
{
if (unit < 10) digArr[startIndex - 1] = 0;
else digArr[startIndex - 1] = unit / 10;
digArr[startIndex] = unit % 10;
}
void SegmentedStopWatch::setDigitsToCurrentTime()
{
setHours();
setMinutes();
setSeconds();
}
void SegmentedStopWatch::setSeconds()
{
set(m_stopWatchDigits, secondsIndexStart, m_seconds);
}
void SegmentedStopWatch::setMinutes()
{
set(m_stopWatchDigits, minutesIndexStart, m_minutes);
}
void SegmentedStopWatch::setHours()
{
set(m_stopWatchDigits, hoursIndexStart, m_hours);
}
/*|----PRIVATE_FUNCTIONS_END----|*/
/*|----SEGMENTED_STOP_WATCH_FUNCTIONS_END----|*/
Source.cpp:
#include"StopWatch.h"
#include"Console.h" //for Console::gotoxy
#include<thread> //for this_thread::sleep_for
int main()
{
std::unique_ptr<Factory> segFact{ std::make_unique<SegmentedStopWatchFactory>() };
std::unique_ptr<Factory> digFact{ std::make_unique<DigitalStopWatchFactory>() };
std::unique_ptr<StopWatch> stoppers[2];
stoppers[0] = segFact->createStopWatch();
stoppers[1] = digFact->createStopWatch();
stoppers[0]->setStopWatchXY(5, 5);
//to test the second stopper simply change stoppers[0] to stoppers[1]
stoppers[0]->countDownFrom(12); //test countdown
//stoppers[0]->countDownFrom(60 * 60 * 60 * 60); //overflow test
/*
stoppers[0]->start();
while (1)
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::milliseconds(200)); //this is only responsible for the refresh rate, the lower the better
}//note that no waiting at all will result in visible reprinting.
*/
/*
int timer = 9;
stoppers[0]->start();
do
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::seconds(1));
}while (--timer);
stoppers[0]->stop(); //test stop
std::this_thread::sleep_for(std::chrono::seconds(3));
timer = 9;
stoppers[0]->start();
do
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::seconds(1));
}while (--timer);
*/
/*
int timer = 9;
stoppers[0]->start();
do
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::seconds(1));
}while (--timer);
stoppers[0]->reset(); //test reset
std::this_thread::sleep_for(std::chrono::seconds(3));
timer = 9;
stoppers[0]->start();
do
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::seconds(1));
}while (--timer);
*/
/*
int timer = 9;
stoppers[0]->start();
do
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::seconds(1));
} while (--timer);
stoppers[0]->lap(); //lap reset
stoppers[0]->stop();
std::this_thread::sleep_for(std::chrono::seconds(3));
timer = 9;
stoppers[0]->start();
do
{
stoppers[0]->updateTime();
stoppers[0]->printTime();
std::this_thread::sleep_for(std::chrono::seconds(1));
} while (--timer);
stoppers[0]->lap();
stoppers[0]->stop();
Console::gotoxy(0, 0);
std::cout << "m_elapsed: " << stoppers[0]->getElapsed() << '\n';
std::cout << "First lap: " << stoppers[0]->getLapTimes()[0] << '\n';
std::cout << "Second lap: " << stoppers[0]->getLapTimes()[1] << '\n';
*/
return 0;
}