#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());
}
void chained_counter_constructors() {
UpDownCounter hi{0, 3}; SHOW_("0", hi.GetValue());
SHOW_("3", hi.GetLimit());
UpDownCounter lo{&hi}; SHOW_("0", lo.GetValue());
SHOW_("0", lo.GetLimit());
SHOW_("2", lo.DownCount(), hi.GetValue());
SHOW_("0", lo.UpCount(), hi.GetValue());
// !!!
// TBD: Test other combinations with chained counters
// !!!
}
#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());
}
#include <string>
void single_counter_max_wrap() {
const auto max = std::numeric_limits<UpDownCounter::value_type>::max();
auto max_init = [](auto m) {
std::ostringstream os;
os << m;
return os.str();
};
const auto almost_max = max_init(max-1);
const auto exactly_max = max_init(max+0);
UpDownCounter ud_0_max{}; SHOW_("0", ud_0_max.GetValue());
SHOW_("0", ud_0_max.GetLimit());
SHOW_(almost_max, ud_0_max.SetValue(max-1), ud_0_max.GetValue());
SHOW_(exactly_max, 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, ud_0_max.DownCount(), ud_0_max.GetValue());
SHOW_(almost_max, 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();
// chained_counter_constructors();
// single_counter_setter();
single_counter_max_wrap();
// single_counter_oflow();
// chained_counters_oflow();
}
#include "Clock.h"
void clock_tick_tests() {
Clock c{"myclock"};
SHOW_("myclock=0.00:00:00", c);
SHOW_("true", c.IsFloor());
SHOW_("myclock=0.00:00:01", c.TickUp());
SHOW_("false", c.IsFloor());
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.IsFloor());
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.IsFloor());
SHOW_("myclock=0.23:59:59", c.Hours(23).Minutes(59).Seconds(59));
SHOW_("myclock=1.00:00:00", c.TickUp());
SHOW_("false", c.IsFloor());
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.IsFloor());
SHOW_("myclock=0.00:00:00", c.Days());
SHOW_("true", c.IsFloor());
}
Clock make_clock() {
return {"clk2", 4, 3, 2, 1};
}
void clock_move_tests() {
Clock c{make_clock()}; SHOW_("clk2=4.03:02:01", c);
SHOW_("clk2=4.03:02:00", c.TickDown());
Clock c2{std::move(c)}; SHOW_("clk2=4.03:02:01", c2);
// c = make_clock(); SHOW_("clk2=4.03:02:01", c);
}
int main() {
std::cout.setf(std::ios::boolalpha);
counter_tests();
clock_tick_tests();
clock_move_tests();
}
#else
#include <cstdlib>
#include <stdexcept>
#include <iostream>
extern void appl();
int main()
{
try {
appl();
return EXIT_SUCCESS;
}
catch (const char* e) {
std::clog << "terminated -- reason: " << e << std::endl;
}
catch (int e) {
std::clog << "terminated -- error code: " << e << std::endl;
}
catch (std::exception &e) {
std::clog << "terminated -- uncaught exception: " << e.what() << std::endl;
}
catch (...) {
std::clog << "terminated -- uncaught non-standard exception" << std::endl;
}
return EXIT_FAILURE;
}
#endif
#include <iostream>
struct MyExcept : public std::logic_error {
MyExcept() : std::logic_error("as defined by user") {}
};
struct OtherExcept {};
void appl() {
// throw (-1);
// throw "not code yet developed";
// throw MyExcept();
// throw OtherExcept();
}
#ifndef ICLOCK_H
#define ICLOCK_H
#include <iosfwd>
class IClock {
public:
virtual ~IClock() =default;
virtual void Print(std::ostream&) const =0;
virtual bool IsCeiling() const =0;
virtual bool IsFloor() 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&&) noexcept; // is implemented // 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 final;
virtual bool IsCeiling() const override final;
virtual bool IsFloor() const override final;
virtual IClock& TickUp() override final
{ if (!IsCeiling()) seconds_.UpCount(); return *this; }
virtual IClock& TickDown() override final
{ if (!IsFloor()) seconds_.DownCount(); return *this; }
};
#endif
#include "Clock.h"
#include <iostream>
#include <iomanip>
Clock::Clock(Clock &&init) noexcept
: name_(init.name_)
, days_(init.days_.GetValue(), init.days_.GetLimit())
, hours_(init.hours_.GetValue(), init.hours_.GetLimit(), &days_)
, minutes_(init.minutes_.GetValue(), init.minutes_.GetLimit(), &hours_)
, seconds_(init.seconds_.GetValue(), init.seconds_.GetLimit(), &minutes_)
{
init.name_ = nullptr;
}
bool Clock::IsCeiling() const {
return (seconds_.GetValue()+1 == 0)
&& (minutes_.GetValue()+1 == 0)
&& (hours_.GetValue()+1 == 0)
&& (days_.GetValue()+1 == 0);
}
bool Clock::IsFloor() 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');
auto name = name_ ? name_ : "?dead?";
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, next_counter)
{}
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_;
}