#define UNIT_TESTING
#ifdef UNIT_TESTING
#include <iostream>
#include <sstream>
#define SHOW_(expect, ...)\
do {\
std::ostringstream result;\
result.copyfmt(std::cout);\
result << (__VA_ARGS__);\
std::cout << __FUNCTION__ << ':' << __LINE__ << '\t'\
<< #__VA_ARGS__ << " --> " << result.str();\
if (result.str() != expect) {\
std::cout << " != " << expect;\
}\
std::cout << std::endl;\
}\
while (0)
#include "UpDownCounter.h"
void single_counter_constructors() {
SHOW_("3", UpDownCounter{3, 5, nullptr}.GetValue());
SHOW_("5", UpDownCounter{3, 5, nullptr}.GetLimit());
// SHOW_("3", UpDownCounter{3, 5, NULL}.GetValue());
// SHOW_("5", UpDownCounter{3, 5, NULL}.GetLimit());
SHOW_("3", UpDownCounter{3, 5}.GetValue());
SHOW_("5", UpDownCounter{3, 5}.GetLimit());
SHOW_("0", UpDownCounter{5, nullptr}.GetValue());
SHOW_("5", UpDownCounter{5, nullptr}.GetLimit());
// SHOW_("0", UpDownCounter{5, NULL}.GetValue());
// SHOW_("5", UpDownCounter{5, NULL}.GetLimit());
SHOW_("0", UpDownCounter{5}.GetValue());
SHOW_("5", UpDownCounter{5}.GetLimit());
SHOW_("0", UpDownCounter{}.GetValue());
SHOW_("0", UpDownCounter{}.GetLimit());
}
#include <limits>
void single_counter_setter() {
UpDownCounter ud_0_max{}; SHOW_("0", ud_0_max.GetValue());
SHOW_("0", ud_0_max.GetLimit());
SHOW_("123", ud_0_max.SetValue(123), ud_0_max.GetValue());
SHOW_("0", ud_0_max.GetLimit());
}
void single_counter_max_wrap() {
const auto max = std::numeric_limits<UpDownCounter::value_type>::max();
std::ostringstream almost_max; almost_max << max-1;
std::ostringstream exactly_max; exactly_max << max+0;
UpDownCounter ud_0_max{}; SHOW_("0", ud_0_max.GetValue());
SHOW_("0", ud_0_max.GetLimit());
SHOW_(almost_max.str(), ud_0_max.SetValue(max-1), ud_0_max.GetValue());
SHOW_(exactly_max.str(), ud_0_max.UpCount(), ud_0_max.GetValue());
SHOW_("0", ud_0_max.UpCount(), ud_0_max.GetValue());
SHOW_("1", ud_0_max.UpCount(), ud_0_max.GetValue());
SHOW_("0", ud_0_max.DownCount(), ud_0_max.GetValue());
SHOW_(exactly_max.str(), ud_0_max.DownCount(), ud_0_max.GetValue());
SHOW_(almost_max.str(), ud_0_max.DownCount(), ud_0_max.GetValue());
}
void single_counter_oflow() {
UpDownCounter ud_3_5{3, 5}; SHOW_("3", ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
SHOW_("4", ud_3_5.UpCount(), ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
SHOW_("0", ud_3_5.UpCount(), ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
SHOW_("1", ud_3_5.UpCount(), ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
SHOW_("0", ud_3_5.DownCount(), ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
SHOW_("4", ud_3_5.DownCount(), ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
SHOW_("3", ud_3_5.DownCount(), ud_3_5.GetValue());
SHOW_("5", ud_3_5.GetLimit());
}
void chained_counters_oflow() {
UpDownCounter hi{3}; SHOW_("0", hi.GetValue());
SHOW_("3", hi.GetLimit());
UpDownCounter lo{4, &hi}; SHOW_("0", lo.GetValue());
SHOW_("4", hi.GetLimit());
SHOW_("1", lo.UpCount(), lo.GetValue());
SHOW_("0", hi.GetValue());
SHOW_("2", lo.UpCount(), lo.GetValue());
SHOW_("0", hi.GetValue());
SHOW_("3", lo.UpCount(), lo.GetValue());
SHOW_("0", hi.GetValue());
SHOW_("0", lo.UpCount(), lo.GetValue());
SHOW_("1", hi.GetValue());
SHOW_("1", lo.UpCount(), lo.GetValue());
SHOW_("1", hi.GetValue());
SHOW_("0", lo.DownCount(), lo.GetValue());
SHOW_("1", hi.GetValue());
SHOW_("3", lo.DownCount(), lo.GetValue());
SHOW_("0", hi.GetValue());
}
void counter_tests() {
single_counter_constructors();
single_counter_setter();
single_counter_max_wrap();
single_counter_oflow();
chained_counters_oflow();
}
#include "Clock.h"
void clock_tests() {
Clock c{"myclock"};
SHOW_("myclock=0.00:00:00", c);
SHOW_("true", c.IsZero());
SHOW_("myclock=0.00:00:01", c.TickUp());
SHOW_("false", c.IsZero());
SHOW_("myclock=0.00:00:02", c.TickUp());
SHOW_("myclock=0.00:00:03", c.TickUp());
SHOW_("myclock=0.00:00:58", c.Seconds(58));
SHOW_("myclock=0.00:00:59", c.TickUp());
SHOW_("myclock=0.00:01:00", c.TickUp());
SHOW_("false", c.IsZero());
SHOW_("myclock=0.00:01:01", c.TickUp());
SHOW_("myclock=0.00:59:59", c.Minutes(59).Seconds(59));
SHOW_("myclock=0.01:00:00", c.TickUp());
SHOW_("false", c.IsZero());
SHOW_("myclock=0.23:59:59", c.Hours(23).Minutes(59).Seconds(59));
SHOW_("myclock=1.00:00:00", c.TickUp());
SHOW_("false", c.IsZero());
SHOW_("myclock=123.00:00:00", c.Days(123));
SHOW_("myclock=122.23:59:59", c.TickDown());
SHOW_("myclock=122.23:00:01", c.Minutes().Seconds(1));
SHOW_("myclock=122.23:00:00", c.TickDown());
SHOW_("myclock=122.22:59:59", c.TickDown());
SHOW_("myclock=122.00:00:00", c.Hours().Minutes().Seconds());
SHOW_("false", c.IsZero());
SHOW_("myclock=0.00:00:00", c.Days());
SHOW_("true", c.IsZero());
}
int main() {
std::cout.setf(std::ios::boolalpha);
counter_tests();
clock_tests();
}
#else
extern void appl();
int main()
{
appl();
}
#endif
#include <iostream>
void appl() {
std::cout << "!!! application not yet developed !!!" << std::endl;
}
#ifndef ICLOCK_H
#define ICLOCK_H
#include <iosfwd>
class IClock {
public:
virtual ~IClock() =default;
virtual void Print(std::ostream&) const =0;
virtual bool IsZero() const =0;
virtual IClock& TickUp() =0;
virtual IClock& TickDown() =0;
};
inline
std::ostream& operator<<(std::ostream& lhs, const IClock& rhs) {
rhs.Print(lhs);
return lhs;
}
#endif
#ifndef CLOCK_H
#define CLOCK_H
#include <cstring>
#include <iosfwd>
#include "IClock.h"
#include "UpDownCounter.h"
class Clock : public IClock {
const char* name_;
UpDownCounter days_;
UpDownCounter hours_;
UpDownCounter minutes_;
UpDownCounter seconds_;
public:
Clock(const char* name,
UpDownCounter::value_type days = 0,
UpDownCounter::value_type hours = 0,
UpDownCounter::value_type minutes = 0,
UpDownCounter::value_type seconds = 0)
: name_{std::strcpy(new char[std::strlen(name)+1], name)}
, days_{days}
, hours_{hours, 24, &days_}
, minutes_{minutes, 60, &hours_}
, seconds_{seconds, 60, &minutes_}
{}
~Clock() { delete[] name_; }
Clock(const Clock&) =delete; // copy c'tor
Clock& operator=(const Clock&) =delete; // copy assignment
Clock(Clock&&) =delete; // move c'tor
Clock& operator=(Clock&&) =delete; // move assignment
Clock& Days(unsigned v = 0) { days_.SetValue(v); return *this; }
Clock& Hours(unsigned v = 0) { hours_.SetValue(v); return *this; }
Clock& Minutes(unsigned v = 0) { minutes_.SetValue(v); return *this; }
Clock& Seconds(unsigned v = 0) { seconds_.SetValue(v); return *this; }
virtual void Print(std::ostream&) const override;
virtual bool IsZero() const override final;
virtual IClock& TickUp() override final { seconds_.UpCount(); return *this; }
virtual IClock& TickDown() override final { seconds_.DownCount(); return *this; }
};
#endif
#include "Clock.h"
#include <iostream>
#include <iomanip>
bool Clock::IsZero() const {
return (seconds_.GetValue() == 0)
&& (minutes_.GetValue() == 0)
&& (hours_.GetValue() == 0)
&& (days_.GetValue() == 0);
}
void Clock::Print(std::ostream& s) const {
std::ostream os{s.rdbuf()};
os.fill('0');
os << name_ << '=' << days_.GetValue() << '.'
<< std::setw(2) << hours_.GetValue() << ':'
<< std::setw(2) << minutes_.GetValue() << ':'
<< std::setw(2) << seconds_.GetValue();
}
#ifndef UP_DOWN_COUNTER_H
#define UP_DOWN_COUNTER_H
#include <cstdint>
#include <type_traits>
class UpDownCounter {
public:
using value_type = std::uint_fast8_t;
static_assert(std::is_integral<value_type>::value
&& std::is_unsigned<value_type>::value,
"designed to work with unsigned integral types only");
private:
value_type value_{};
const value_type max_value_{};
UpDownCounter *next_counter_{};
public:
UpDownCounter() =default;
UpDownCounter(const UpDownCounter&) =delete;
UpDownCounter(UpDownCounter&&) =delete;
UpDownCounter& operator=(const UpDownCounter&) =delete;
UpDownCounter& operator=(UpDownCounter&&) =delete;
UpDownCounter(value_type value, value_type max_value, UpDownCounter* next_counter)
: value_{value}
, max_value_{max_value}
, next_counter_{next_counter}
{}
UpDownCounter(value_type max_value, UpDownCounter* next_counter)
: UpDownCounter(0, max_value, next_counter)
{}
UpDownCounter(value_type value, value_type max_value)
: UpDownCounter(value, max_value, nullptr)
{}
UpDownCounter(value_type max_value)
: UpDownCounter(0, max_value, nullptr)
{}
UpDownCounter(UpDownCounter* next_counter_)
: UpDownCounter(0, 0, nullptr)
{}
void SetValue(value_type value) { value_ = value; }
int GetValue() const { return value_; }
int GetLimit() const { return max_value_; }
void UpCount();
void DownCount();
};
#endif
#include "UpDownCounter.h"
void UpDownCounter::UpCount() {
if (++value_ == max_value_) {
value_ = 0;
if (next_counter_) {
next_counter_->UpCount();
}
}
}
void UpDownCounter::DownCount() {
if (value_ == 0) {
value_ = max_value_;
if (next_counter_) {
next_counter_->DownCount();
}
}
--value_;
}
Back Down to Earth: An Up-Down Counter and Clock
------------------------------------------------
TBD
Review the code and maybe make some modifications
-------------------------------------------------
Review the two classes:
* `Clock` (files `Clock.h` and `Clock.cpp`) and
* `UpDownCounter` (files `UpDownCounter.h and `UpDownCounter.cpp`)
Run the tests for the `UpDownCounter` in `main`:
* You may temporarily comment-out the call to`clock_tests()`.
* Optionally also temporarily comment-out any testsFrom `counter_tests`
you don't want for the moment, so that you get less output.
Then answer the following questions:
* How many ways exist to construct an `UpDownCounter` and what is their
differrence?
* Described the exact overflow behavior of an `UpDownCounter`, especially:
* What is the maximum `value_` can reach if `max_value_` is any value
other than zero and ...
* ... to which value does a counter "wrap around" DOWNWARDS from zero
in this case?
* What is the maximum `value_` can reach if `max_value_` IS zero and ...
* ... to which value does a counter "wrap around" DOWNWARDS from zero
in this case?
* Why is it so important that the type of the values held in a counter is
an unsigned integral?
* Which is the highest count that a `Clock` can reach?
Some modifications ypu may want to make to the code:
TBD