Reputation: 4839
Consider this output:
Current time: 6:30 pm
Current time: 18:30
Current time: evening.
Current time: evening (for many it is dinner time, but many eat dinner later).
Note that the last two have a period, while the first two do not. I obtained this desired output with the System::displayCurrentTime
member function from the code below:
#include <iostream>
#include <string>
#include <memory>
class TimeDisplay {
public:
virtual std::string tell() const = 0;
virtual std::string tellMaybeWithPeriod() const = 0;
};
class ClockDisplay12Hours : public TimeDisplay { // #1
std::string tell() const override {return "6:30 pm";}
std::string tellMaybeWithPeriod() const override {return tell();}
};
class ClockDisplay24Hours : public TimeDisplay { // #2
std::string tell() const override {return "18:30";}
std::string tellMaybeWithPeriod() const override {return tell();}
};
class DescriptiveTimeDisplay : public TimeDisplay { // #3
std::string tell() const override {return "evening";}
std::string tellMaybeWithPeriod() const override {return tell() + ".";}
};
class CrazyDescriptiveTimeDisplay : public TimeDisplay { // #4
std::string tell() const override {return "evening (for many it is dinner time, but many eat dinner later)";}
std::string tellMaybeWithPeriod() const override {return tell() + ".";}
};
struct System {
static std::shared_ptr<TimeDisplay> timeDisplay;
static std::string timeAsString() {return timeDisplay->tell();}
static std::string timeAsStringMaybeWithPeriod() {return timeDisplay->tellMaybeWithPeriod();}
// #3 and #4 will have a period, the others will not.
static void displayCurrentTime (std::shared_ptr<TimeDisplay> t) {
timeDisplay = t;
std::cout << "Current time: " << System::timeAsStringMaybeWithPeriod() << '\n';
}
static void foo (std::shared_ptr<TimeDisplay>) {} // #1 and #3 will have a period, the others will not.
static void bar (std::shared_ptr<TimeDisplay>) {} // #1, #2, and #4 will have a period, the others will not.
static void baz (std::shared_ptr<TimeDisplay>) {} // #2 will have a period, the others will not
};
std::shared_ptr<TimeDisplay> System::timeDisplay;
int main() {
const std::shared_ptr<TimeDisplay> clocks[] = {std::make_shared<ClockDisplay12Hours>(), std::make_shared<ClockDisplay24Hours>(),
std::make_shared<DescriptiveTimeDisplay>(), std::make_shared<CrazyDescriptiveTimeDisplay>()};
for (std::shared_ptr<TimeDisplay> t : clocks)
System::displayCurrentTime(t);
}
This is not terribly messy, but not note that the next functions to implement foo
, bar
, baz
want the periods with different derived classes of TimeDisplay
, and there are actually much more than 4 such derived classes, and also more than 3 new member functions to take care of. Is there a cleaner more elegant way to handle these upcoming member functions than to write out new virtual functions for each of foo
, bar
, baz
, etc... along with which will get the period and which will not? Use templates somehow (e.g. renaming the derived classes Derived<0>
, Derived<1>
, etc... and then using these compile-time integers to fit the rules stated in the comments above)? Of maybe avoid templates and do something else?
Upvotes: 0
Views: 61
Reputation: 4839
Thanks to simon's idea, I now got the ideal solution I wanted using the following traits:
template <int, int> struct PeriodOrNoPeriod;
template <> struct PeriodOrNoPeriod<0,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<0,1> : std::false_type {};
template <> struct PeriodOrNoPeriod<0,2> : std::true_type {};
template <> struct PeriodOrNoPeriod<0,3> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<1,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,2> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,3> : std::false_type {};
template <> struct PeriodOrNoPeriod<2,0> : std::true_type {};
template <> struct PeriodOrNoPeriod<2,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<2,2> : std::false_type {};
template <> struct PeriodOrNoPeriod<2,3> : std::true_type {};
template <> struct PeriodOrNoPeriod<3,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<3,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<3,2> : std::false_type {};
template <> struct PeriodOrNoPeriod<3,3> : std::false_type {};
and the function to handle all cases at once:
template <System::Action A, int N>
struct System::SetTimeDisplay {
static void execute (TimeDisplay::Mode mode) {
constexpr TimeDisplay::Mode M = static_cast<TimeDisplay::Mode>(N);
if (mode == M)
timeDisplay = std::make_unique<TimeDisplayClass<M, PeriodOrNoPeriod<A,M>::value>>();
else
SetTimeDisplay<A, N+1>::execute(mode);
}
};
template <System::Action A>
struct System::SetTimeDisplay<A, TimeDisplay::NumTimeDiplayModes> {
static void execute (TimeDisplay::Mode) {} // End of recursion
};
template <System::Action A>
inline void System::action (TimeDisplay::Mode mode) {
SetTimeDisplay<A,0>::execute(mode);
finalAction<A>();
}
Full solution here:
Upvotes: 0
Reputation: 391
It can be done by giving a bool template parameter to the four clock display classes. e.g.
template <bool P>
class DescriptiveTimeDisplay : public TimeDisplay { // #3
std::string tell() const override { return "evening"; }
std::string tellMaybeWithPeriod() const override { return tell() + (P ? "." : ""); }
};
and control whether the period will be displayed by intantiate the class as, e.g.
std::make_shared<DescriptiveTimeDisplay<true>>()
for each of the four functions displayCurrentTime
, foo
, bar
, baz
, you can control their display format by instantiating the four TimeDisplay
child classes with different bool template parameters.
Upvotes: 1