// Main Program and Application for ALL five Exercises
// ===================================================
// What this program does is configured by enabling
// one or more of the following #define-s
//
// Exercise 1: Manual Testing: no #define-s enabled
// Unit-Testing: enable #define TEST1
// Exercise 2: Manual Testing: #define-s EXCEPTION
// Unit-Testing: enable #define TEST2
// Exercise 3: Manual Testing: #define-s OBSERVER
// Unit-Testing: enable #define TEST3
// Exercise 4: Manual Testing: #define-s CONTAINER
// Unit-Testing: enable #define TEST4
// Exercise 5: Manual Testing: enable THREAD
// Unit-Testing: (none provided)
// NOTE: #define-s for manual testing accumulate, i.e.
// they are meant to be enabled and stay enabled
// as the exercise progresses. But if ANY of
// TEST1, TEST2, TEST3, TEST4 is enabled ONLY
// the respective unit-testing takes place.
// enable manual test cases
// ---------------------------------------------
#define EXCEPTION
#define OBSERVER
//#define CONTAINER
//#define THREAD
// enable/disable unit tests
// ---------------------------------------------
//#define TEST1
//#define TEST2
#define TEST3
//#define TEST4
#include <cassert>
#include <iostream>
#include <sstream>
#include "limit_counter.h"
#include "overflow_counter.h"
#include "clock.h"
#if defined(TEST1) // ------ Unit-Testing for Exercise 1
int main() {
Clock c{23, 59, 58};
std::ostringstream expect;
// should only step up the seconds here
c.Update(expect);
assert(expect.str() == "Clock [hh:mm:ss] 23:59:59\n");
expect.str("");
// should step up the seconds which resets and in turn
// steps up the minutes which resets and in turn steps
// up the hours which resets.
// MOTE: This test will FAIL if LimitCounter::Count is
// not virtual and properly overwritten in
// OverflowCounter::Count
c.Update(expect);
assert(expect.str() == "Clock [hh:mm:ss] 00:00:00\n");
expect.str("");
// should only step up the seconds here
c.Update(expect);
assert(expect.str() == "Clock [hh:mm:ss] 00:00:01\n");
expect.str("");
// should only step up the seconds here
c.Update(expect);
assert(expect.str() == "Clock [hh:mm:ss] 00:00:02\n");
expect.str("");
c.SetHours(3);
c.SetMinutes(59);
c.SetSeconds(59);
// should reset seconds and minutes, stepping up hours
// NOTE: This one will FAIL if LimitCounter::Count uses
// plain Reset() to set value_ to zero. Correct
// implementation must use LimitCounter::Reset()
// to avoid the whole counter chain uop to hours
// is reset to zero
c.Update(expect);
assert(expect.str() == "Clock [hh:mm:ss] 04:00:00\n");
expect.str("");
c.Reset();
c.Update(expect);
// Note:
// c.Update() FIRST increments the Clock and only then
// shows "hh:mm:ss" so the expection is not "00:00:00"
assert(expect.str() == "Clock [hh:mm:ss] 00:00:01\n");
expect.str("");
std::cout << "passed all tests of clock functionality\n";
}
#elif defined(TEST2) // ---- Unit-Testing for Exercise 2
int main() {
try {
LimitCounter lc{0, 10};
lc.SetValue(11);
assert(!"LimitCounter exeception expected");
}
catch (LimitCounter::ValueOutOfRange &ex) {}
catch (...) {
assert(!"not the expected exception");
}
}
#elif defined(TEST3) // ---- Unit-Testing for Exercise 3
#include <type_traits>
#include "array.h"
int main() {
Array<int, 5> test_obj{0};
// -------------------------------------------------------------
assert((std::is_same<decltype(test_obj)::value_type, int>{}));
// -----------------------------------------^^^^^^^^^^----------
// --------------------------------
assert(test_obj.max_size() == 5);
// ----------------^^^^^^^^^^------
for (auto v : { 7, 14, 12, 2, 5 }) {
test_obj.Insert(v);
}
// ------------------------
assert(test_obj[0] == 7);
assert(test_obj[1] == 14);
assert(test_obj[2] == 12);
assert(test_obj[3] == 2);
assert(test_obj[4] == 5);
// ---------------^^^------
// -------------------------------------
assert(test_obj.Insert(111) == false);
// ^^^^^^ ^^^^^
test_obj.Erase(12);
assert(test_obj.Insert(111) == true);
// ----------------^^^^^^---------^^^^
// -----------------------------------------------------
assert((std::is_same<decltype(test_obj[0]), int&>{}));
// ---------------------^^^^^^^^---------^^^---^^^^------
// ----------------------------------------------------------------
auto const& const_test_obj = test_obj;
// ^^^^^
assert((std::is_same<decltype(const_test_obj[0]), int const&>{}));
// ---------------------^^^^^^^^---------------^^^---^^^^^^^^^^-----
// ------------------------
assert(test_obj[6] == 0);
test_obj[6] = 999;
assert(test_obj[6] == 0);
// ---------------!!!------
std::cout << "Congrats! all tests passed" << std::endl;
}
#elif defined(TEST4) // ---- Unit-Testing for Exercise 4
// ... TBD
#else // ----------------------- Manual Testing selected
#include <cctype>
#include <iostream>
void ShowValue() {
std::cout << std::endl;
}
template <typename T>
void ShowValue(T text)
{
std::cout << text << std::endl;
}
template <typename T, typename... Other>
void ShowValue(T text, Other... other)
{
std::cout << text << " ";
ShowValue(other...);
}
char GetCommand()
{
std::cout << "::> " << std::flush;
char cmd;
while (std::cin.get(cmd)) {
if (cmd == '\n') continue;
std::cin.ignore(99999, '\n');
if (std::isupper(cmd = std::toupper(cmd))) {
return cmd;
}
}
return 'Q';
}
// =====================================================
// DEFINITION of Actual Application (will usually have
// its own application.h file but is included directly
// for convenient use in onlineGDB)
#include <memory>
#include <string>
#include "clock.h"
#include "iclock_factory.h"
class Application {
private:
IClockFactory& clock_factory_;
std::shared_ptr<Clock> clock_;
public:
explicit Application(IClockFactory& clock_factory)
: clock_factory_{clock_factory}
{
}
void SetClock(const std::shared_ptr<Clock>& clock)
{
// if clock == nullptr) throw "error";
// if (clock == nullptr) throw std::string("no clock service available");
using namespace std::string_literals;
if (clock == nullptr) throw "no clock service available"s;
clock_ = clock;
}
void Run();
};
// =====================================================
// System Manager Services (will usually have its own
// header and implementation file but included directly
// for convenient use in onlineGDB).
#include "iclock_factory.h"
#ifdef THREAD
#include "timer.h"
#endif
constexpr unsigned kStartHours = 23;
constexpr unsigned kStartMinutes = 59;
constexpr unsigned kStartSeconds = 55;
class SystemManager : public IClockFactory {
private:
std::shared_ptr<Clock> clock_;
#ifdef THREAD
std::shared_ptr<Timer> timer_;
#endif
std::shared_ptr<Application> application_;
public:
SystemManager() {
Create();
BuildRelations();
}
void Execute() const {
ShowValue("Clock Observer ...");
application_->Run();
}
#ifdef THREAD
timer_->Wait();
#endif
std::shared_ptr<Clock> GetNewClock(unsigned hours,
unsigned minutes,
unsigned seconds) override {
return std::make_shared<Clock>(hours, minutes, seconds);
}
private:
void Create() {
clock_.reset(new Clock{kStartHours, kStartMinutes, kStartSeconds});
application_.reset(new Application{*this});
}
void BuildRelations() const {
application_->SetClock(clock_);
}
};
// =====================================================
// IMPLEMENTATION of Actual Application (will usually
// application.cpp file but included directly for
// convenient use in onlineGDB)
void Application::Run() {
while (true) {
ShowValue("\n");
#ifndef OBSERVER
#ifndef CONTAINER
ShowValue("'A': Tick clock");
ShowValue("'B': Reset clock");
#ifdef EXCEPTION
ShowValue("'C': Set IClock-Pointer to nullptr");
#endif
#endif
#endif
#if defined(OBSERVER) || defined(CONTAINER)
ShowValue("'A': Tick all clocks");
ShowValue("'B': Attach a clock");
ShowValue("'C': Detach a clock");
ShowValue("'D': Reset clock");
ShowValue("'E': Attach five different initialized clocks");
#endif
#ifdef THREAD
ShowValue("'F': Start the timer thread");
#endif
ShowValue("'Q': Quit this program");
ShowValue();
switch (GetCommand()) {
#ifndef OBSERVER
#ifndef CONTAINER
case 'A':
clock_->Update(std::cout);
break;
case 'B':
clock_->Reset();
break;
#ifdef EXCEPTION
case 'C':
SetClock(nullptr);
break;
#endif
#endif
#endif
#if defined(OBSERVER) || defined(CONTAINER)
case 'A':
// timer_->Tick();
break;
case 'B':
// timer_->Attach(clock_);
break;
case 'C':
// timer_->Detach(clock_);
break;
case 'D':
clock_->Reset();
break;
case 'E':
// timer_->Attach(clock_factory_.GetNewClock(1, 0, 0));
// timer_->Attach(clock_factory_.GetNewClock(2, 0, 0));
// timer_->Attach(clock_factory_.GetNewClock(3, 0, 0));
// timer_->Attach(clock_factory_.GetNewClock(4, 50, 40));
// timer_->Attach(clock_factory_.GetNewClock(5, 59, 50));
break;
#endif
#ifdef THREAD
case 'F':
timer_->Start();
break;
#endif
case 'Q':
#ifdef THREAD
timer_->Stop();
#endif
return;
default:
break;
}
}
}
#include <string>
int main() {
const std::unique_ptr<SystemManager> system_manager{new SystemManager};
try {
system_manager->Execute();
}
catch (const std::string &ex) {
std::clog << "Execution Terminated (details: " << ex << ")\n";
}
catch (const char *ex) {
std::clog << "Execution Terminated: " << ex << ".\n";
}
catch (const std::exception &ex) {
std::clog << "Execution Terminated by '" << ex.what() << "' exception\n";
}
catch (...) {
std::clog << "Execution Terminated by unknkwon exception\n";
}
}
#endif
#include "limit_counter.h"
#include <string>
#include <stdexcept>
LimitCounter::LimitCounter (const unsigned value, const unsigned limit)
: value_{value}, limit_{limit}
{
}
void
LimitCounter::SetValue (const unsigned value)
{
value_ = value;
}
unsigned
LimitCounter::GetValue () const
{
return value_;
}
bool
LimitCounter::Count ()
{
const auto temp_value = GetValue () + 1;
if (temp_value >= limit_)
{
LimitCounter::Reset ();
return true;
}
SetValue (temp_value);
return false;
}
void
LimitCounter::Reset ()
{
value_ = 0;
}
#ifndef LIMIT_COUNTER_H
#define LIMIT_COUNTER_H
#include <stdexcept>
class LimitCounter {
private:
unsigned value_;
const unsigned limit_;
public:
LimitCounter(unsigned value, unsigned limit);
virtual ~LimitCounter() = default;
void SetValue(unsigned value);
unsigned GetValue() const;
virtual bool Count();
virtual void Reset();
};
#endif // include guard
#include "overflow_counter.h"
OverflowCounter::OverflowCounter(const unsigned value, const unsigned limit,
LimitCounter& next_counter)
: LimitCounter{value, limit}, next_counter_{next_counter}
{
}
void OverflowCounter::Reset()
{
LimitCounter::Reset();
next_counter_.Reset();
}
bool OverflowCounter::Count()
{
const auto is_overflow = LimitCounter::Count();
if (is_overflow) {
next_counter_.Count();
}
return is_overflow;
}
#ifndef OVERFLOW_COUNTER_H
#define OVERFLOW_COUNTER_H
#include "limit_counter.h"
class OverflowCounter : public LimitCounter {
private:
LimitCounter& next_counter_;
public:
OverflowCounter(unsigned value, unsigned limit, LimitCounter& next_counter);
void Reset() override;
bool Count() override;
};
#endif // include guard
#ifndef CLOCK_H
#define CLOCK_H
#include <iosfwd>
#include "limit_counter.h"
#include "overflow_counter.h"
class Clock {
private:
LimitCounter hours_;
OverflowCounter minutes_;
OverflowCounter seconds_;
public:
Clock(unsigned hours, unsigned minutes, unsigned seconds);
void SetHours(unsigned hours);
void SetMinutes(unsigned minutes);
void SetSeconds(unsigned seconds);
unsigned GetHours() const;
unsigned GetMinutes() const;
unsigned GetSeconds() const;
void Reset();
void Update(std::ostream &os);
void Show(std::ostream&) const;
};
#endif // include guard
#include "clock.h"
#include <iomanip>
#include <iostream>
Clock::Clock(const unsigned hours, const unsigned minutes,
const unsigned seconds)
: hours_{hours, 24},
minutes_{minutes, 60, hours_},
seconds_{seconds, 60, minutes_}
{
}
void Clock::SetHours(const unsigned hours) { hours_.SetValue(hours); }
void Clock::SetMinutes(const unsigned minutes) { minutes_.SetValue(minutes); }
void Clock::SetSeconds(const unsigned seconds) { seconds_.SetValue(seconds); }
unsigned Clock::GetHours() const { return hours_.GetValue(); }
unsigned Clock::GetMinutes() const { return minutes_.GetValue(); }
unsigned Clock::GetSeconds() const { return seconds_.GetValue(); }
void Clock::Reset() {
seconds_.Reset();
}
void Clock::Update(std::ostream& os)
{
seconds_.Count();
Show(os);
}
void Clock::Show(std::ostream& os) const
{
os << "Clock [hh:mm:ss] " << std::setfill('0') << std::setw(2)
<< GetHours() << ":" << std::setfill('0') << std::setw(2)
<< GetMinutes() << ":" << std::setfill('0') << std::setw(2)
<< GetSeconds() << "\n";
}
#ifndef ICLOCK_FACTORY_H
#define ICLOCK_FACTORY_H
#include <memory>
#include "clock.h"
class IClockFactory {
public:
virtual ~IClockFactory() = default;
virtual
std::shared_ptr<Clock> GetNewClock(unsigned hours,
unsigned minutes,
unsigned seconds) = 0;
};
#endif // include guard
#ifndef IOBSERVER_H
#define IOBSERVER_H
class IObserver {
public:
virtual ~IObserver() = default;
virtual void Update() = 0;
};
#endif // include guard
#ifndef ARRAY_H
#define ARRAY_H
#include <type_traits>
template <typename T, unsigned N>
class Array {
public:
using value_type = T;
static constexpr auto max_size() { return N; }
private:
T init_value_; // if this value is held by `array` then it is "free slot"
T array_[N]{}; // fixeds siz (limits the number of entries that can be held)
public:
explicit Array(T init_value);
virtual ~Array() = default;
bool Insert(T element);
void Erase(T element);
T& operator[](unsigned index);
const T& operator[](unsigned index) const;
};
template <typename T, unsigned N>
Array<T, N>::Array(T init_value) : init_value_{init_value}
{
for (auto &e : array_) {
e = init_value;
}
}
template <typename T, unsigned N>
bool Array<T, N>::Insert(T element)
{
for (auto &e : array_) {
if (e == init_value_) {
e = element;
return true;
}
}
// caller my look at this if interested if successful
return false;
}
template <typename T, unsigned N>
void Array<T, N>::Erase(T element)
{
for (auto &e : array_) {
if (e == element) {
e = init_value_;
// remove just one or all(?) ... hmm, all (hope that's ok)
}
}
// no indication: if not found then it's not there - finito!
}
template <typename T, unsigned N>
T& Array<T, N>::operator[](unsigned index)
{
static_assert(std::is_unsigned<decltype(index)>::value,
"for signed argument types add test for >= 0 below");
if (index < max_size()) {
return array_[index];
}
static value_type dummy_result = init_value_;
dummy_result = init_value_;
return dummy_result;
}
template <typename T, unsigned N>
const T& Array<T, N>::operator[](unsigned index) const
{
return const_cast<Array*>(this)->operator[](index);
}
#endif // include guard