#include "Clock.h"
extern void appl(IClock&);
int main()
{
std::cout.setf(std::ios::boolalpha);
Clock c{};
appl(c);
}
#include <iostream>
#define SHOW(...)\
(void)(std::cout << __FILE__ << ':' << __LINE__ << '\t'\
<< #__VA_ARGS__ << " --> " << (__VA_ARGS__)\
<< std::endl)
#include "IClock.h"
void appl(IClock& c) {
c.TickUp(); SHOW(c);
c.TickUp(); SHOW(c);
c.TickUp(); SHOW(c);
c.Set(0, 0, 58); SHOW(c);
c.TickUp(); SHOW(c);
c.TickUp(); SHOW(c);
c.TickUp(); SHOW(c);
c.Set(0, 59, 58); SHOW(c);
c.TickUp(); SHOW(c);
c.TickUp(); SHOW(c);
}
#ifndef ICLOCK_H
#define ICLOCK_H
#include <iosfwd>
class IClock {
public:
virtual ~IClock() =default;
virtual void Set(int, int, int) =0;
virtual void TickUp(int = 1) =0;
virtual void Print(std::ostream&) const =0;
};
inline
std::ostream& operator<<(std::ostream& lhs, const IClock& rhs) {
rhs.Print(lhs);
return lhs;
}
#endif
#ifndef CLOCK_H
#define CLOCK_H
#include <iosfwd>
#include <cstring>
#include <iostream>
#include "IClock.h"
#include "LimitCounter.h"
#include "OverflowCounter.h"
class Clock : public IClock {
LimitCounter hours_;
OverflowCounter minutes_;
OverflowCounter seconds_;
public:
Clock(int hours = 0, int minutes = 0, int seconds = 0)
: hours_{24}
, minutes_{60, hours_}
, seconds_{60, minutes_}
{
Set(hours, minutes, seconds);
}
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
virtual void Set(int hours, int minutes, int seconds) override {
hours_.SetValue(hours);
minutes_.SetValue(minutes);
seconds_.SetValue(seconds);
}
virtual void TickUp(int seconds) override { seconds_.Count(seconds); }
virtual void Print(std::ostream&) const override;
};
#endif
#include "Clock.h"
#include <iostream>
#include <iomanip>
void Clock::Print(std::ostream& s) const {
std::ostream os{s.rdbuf()};
os.fill('0');
os << std::setw(2) << hours_.GetValue() << ':'
<< std::setw(2) << minutes_.GetValue() << ':'
<< std::setw(2) << seconds_.GetValue();
}
#ifndef LIMIT_COUNTER_H
#define LIMIT_COUNTER_H
#include <limits>
class LimitCounter {
private:
int value_{};
const int max_value_{};
public:
LimitCounter(int max_value = std::numeric_limits<int>::max())
: value_{0}
, max_value_{max_value}
{}
void SetValue(int value) { value_ = value; }
int GetValue() const { return value_; }
void Count(int amount);
virtual bool Count();
virtual void Reset();
};
#endif
#include "LimitCounter.h"
void LimitCounter::Count(int amount) {
while (amount-- > 0) {
Count();
}
}
bool LimitCounter::Count() {
if (++value_ >= max_value_) {
value_ = 0;
return true;
}
return false;
}
void LimitCounter::Reset() {
value_ = 0;
}
#ifndef OVERFLOW_COUNTER_H
#define OVERFLOW_COUNTER_H
#include "LimitCounter.h"
class OverflowCounter : public LimitCounter {
LimitCounter& next_counter_;
public:
OverflowCounter(const OverflowCounter&) =delete;
OverflowCounter& operator=(const OverflowCounter&) =delete;
OverflowCounter(OverflowCounter&&) =delete;
OverflowCounter& operator=(OverflowCounter&&) =delete;
OverflowCounter(int max_value, LimitCounter& next_counter)
: LimitCounter{max_value}
, next_counter_{next_counter}
{}
using LimitCounter::Count;
virtual bool Count() override;
virtual void Reset() override;
};
#endif
#include "OverflowCounter.h"
bool OverflowCounter::Count() {
const auto overflowed = LimitCounter::Count();
if (overflowed) {
next_counter_.Count();
}
return overflowed;
}
void OverflowCounter::Reset() {
LimitCounter::Reset();
next_counter_.Reset();
}
clock-3:
g++ -std=c++14 *.cpp -o $@
run: clock-3
./clock-3
clean:
-rm -f *.o a.out core clock-3
zip:
zip ../clock-3.zip *.txt *.cpp *.h
Separate counting into two classes
----------------------------------
Using references instead of pointers to chain the counters.
To advance to the next step apply the following changes:
* Change the class design to apply the NVI-Idiom (= Non Virtual
Interface)
* Make the currently `public` member function
`LimitCounter::Count` and `LimitCounter::Reset` NON-`virtual`
and `final`.
* From inside these member functions, when the counter value is
reset call new `virtual` member functions, say
* `LimitCounter::HasOverflowed` and
* `LimitCounter::HasResetted`
* Implement both EMPTY in class `LimitCounter`.
* Overwrite both in class `OverflowCounter` to simply call
* next_counter_.Count() or
* next_counter_.Reset() respectively
Note: These two functions may (and should) be private` but can
still be overriden in the derived class.
=================================================================
Optional:
* Consider the case that
* there may be many more such "extension points" like
`LimitCounter::HasOverflowed` and `LimitCounter::HasResetted`
* most of which stay unused in the derived classes.
* What are the Pros and Cons?