#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) {
SHOW(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);
c.TickUp(60); SHOW(c);
c.TickUp(600); 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 std::ostream& Print(std::ostream&) const =0;
};
inline
std::ostream& operator<<(std::ostream& lhs, const IClock& rhs) {
return rhs.Print(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 std::ostream& Print(std::ostream&) const override;
};
#endif
#include "Clock.h"
#include <iostream>
#include <iomanip>
std::ostream& 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();
return s;
}
#ifndef ICOUNTER_H
#define ICOUNTER_H
class ICounter {
public:
virtual ~ICounter() =default;
virtual void Count() =0;
virtual void Reset() =0;
};
#endif
#ifndef COUNTER_H
#define COUNTER_H
#include "ICounter.h"
template<typename Details>
class CounterBase : public ICounter, private Details {
private:
int value_{};
const int max_value_{};
using Details::WillOverflow;
using Details::WillReset;
using Details::HasOverflowed;
using Details::HasResetted;
public:
CounterBase(int max_value)
: value_{0}
, max_value_{max_value}
{}
CounterBase(int max_value, ICounter& details_arg)
: Details{details_arg}
, value_{0}
, max_value_{max_value}
{}
void SetValue(int value) { value_ = value; }
int GetValue() const { return value_; }
void Count(int amount);
virtual void Count() override final;
virtual void Reset() override final;
};
template<typename Details>
void CounterBase<Details>::Count(int amount) {
while (amount-- > 0) {
Count();
}
}
template<typename Details>
void CounterBase<Details>::Count() {
if (++value_ >= max_value_) {
WillOverflow();
value_ = 0;
HasOverflowed();
}
}
template<typename Details>
void CounterBase<Details>::Reset() {
WillReset();
value_ = 0;
HasResetted();
}
class LimitCounter_Details {
protected:
void WillOverflow() {}
void WillReset() {}
void HasOverflowed() {}
void HasResetted() {}
};
using LimitCounter = CounterBase<LimitCounter_Details>;
#endif
#ifndef OVERFLOW_COUNTER_H
#define OVERFLOW_COUNTER_H
#include "LimitCounter.h"
class OverflowCounter_Details : protected LimitCounter_Details {
ICounter& next_counter_;
protected:
OverflowCounter_Details(ICounter& next_counter)
: LimitCounter_Details{}
, next_counter_{next_counter}
{}
void HasOverflowed() { next_counter_.Count(); }
void HasResetted() { next_counter_.Reset(); }
};
using OverflowCounter = CounterBase<OverflowCounter_Details>;
#endif
clock-6x:
g++ -std=c++14 *.cpp -o $@
run: clock-6x
./clock-6x
clean:
-rm -f *.o a.out core clock-6x
zip:
zip ../clock-6x.zip *.txt *.cpp *.h
Fill extension points via Template Base class
---------------------------------------------
Compared to the traditional "template methode pattern"
(according to the name chosen by the authors of the widely known
"GoF-Book"), which is filling extension points by overriding
inherited member functions in derived classes, an alternative
technique is shown here:
It is based on calling member functions from a base class which
is specified via a template argument, so that different base
classes can be used for different needs.
In the past this has sometimes been called "inverted template
method pattern" but meanwhile is more often called "policy based
design".
-----------------------------------------------------------------
NOTE: Applying this to the `LimitCounter` / `OverflowCounter`
example is a little bit more complicated as the simplest
possible form. Read on, it will be explained now.
-----------------------------------------------------------------
TBD: describe the purpose of the `Details` class
TBD: describe the initialization problem