#include <iostream>
#include <string>
#include <memory>
#include <type_traits>
class TimeDisplay {
public:
enum Mode {ClockTime12Hours, ClockTime24Hours, DescriptiveTime, CrazyDescriptiveTime, NumTimeDiplayModes};
virtual ~TimeDisplay() = default;
virtual std::string tell() const = 0;
virtual std::string tellMaybeWithPeriod() const = 0;
};
template <typename> class TimeDisplayCRTP;
template <TimeDisplay::Mode, bool = false> class TimeDisplayClass;
template <TimeDisplay::Mode M, bool B>
class TimeDisplayCRTP<TimeDisplayClass<M, B>> : virtual public TimeDisplay {
virtual std::string tellMaybeWithPeriod() const override {return tell() + (B ? "." : "");}
};
template <bool B>
class TimeDisplayClass<TimeDisplay::ClockTime12Hours, B> : public TimeDisplayCRTP<TimeDisplayClass<TimeDisplay::ClockTime12Hours, B>> {
virtual std::string tell() const override {return "6:30 pm";}
};
template <bool B>
class TimeDisplayClass<TimeDisplay::ClockTime24Hours, B> : public TimeDisplayCRTP<TimeDisplayClass<TimeDisplay::ClockTime24Hours, B>> {
virtual std::string tell() const override {return "18:30";}
};
template <bool B>
class TimeDisplayClass<TimeDisplay::DescriptiveTime, B> : public TimeDisplayCRTP<TimeDisplayClass<TimeDisplay::DescriptiveTime, B>> {
virtual std::string tell() const override {return "evening";}
};
template <bool B>
class TimeDisplayClass<TimeDisplay::CrazyDescriptiveTime, B> : public TimeDisplayCRTP<TimeDisplayClass<TimeDisplay::CrazyDescriptiveTime, B>> {
virtual std::string tell() const override {return "evening (for many it is dinner time, but many eat dinner later)";}
};
class System {
public:
enum Action {DisplayTime, Foo, Bar, Baz};
private:
template <Action, int> struct SetTimeDisplay;
static std::unique_ptr<TimeDisplay> timeDisplay;
public:
static std::string timeAsString() {return timeDisplay->tell();}
static std::string timeAsStringMaybeWithPeriod() {return timeDisplay->tellMaybeWithPeriod();}
template <Action> static inline void action (TimeDisplay::Mode);
private:
template <Action> static inline void finalAction();
};
std::unique_ptr<TimeDisplay> System::timeDisplay;
template <System::Action, TimeDisplay::Mode> struct PeriodOrNoPeriod;
template <> struct PeriodOrNoPeriod<System::DisplayTime, TimeDisplay::ClockTime12Hours> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::DisplayTime, TimeDisplay::ClockTime24Hours> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::DisplayTime, TimeDisplay::DescriptiveTime> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::DisplayTime, TimeDisplay::CrazyDescriptiveTime> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Foo, TimeDisplay::ClockTime12Hours> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::Foo, TimeDisplay::ClockTime24Hours> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Foo, TimeDisplay::DescriptiveTime> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Foo, TimeDisplay::CrazyDescriptiveTime> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::Bar, TimeDisplay::ClockTime12Hours> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Bar, TimeDisplay::ClockTime24Hours> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Bar, TimeDisplay::DescriptiveTime> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::Bar, TimeDisplay::CrazyDescriptiveTime> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Baz, TimeDisplay::ClockTime12Hours> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::Baz, TimeDisplay::ClockTime24Hours> : std::true_type {};
template <> struct PeriodOrNoPeriod<System::Baz, TimeDisplay::DescriptiveTime> : std::false_type {};
template <> struct PeriodOrNoPeriod<System::Baz, TimeDisplay::CrazyDescriptiveTime> : std::false_type {};
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>>(); // 'timeDisplay' is a static data member of System, so System::SetTimeDisplay<A,N> has access to it.
else
SetTimeDisplay<A, N+1>::execute(mode);
}
};
template <System::Action A>
struct System::SetTimeDisplay<A, TimeDisplay::NumTimeDiplayModes> {
static void execute (TimeDisplay::Mode) {std::cout << "Error! End of recursion reached.\n";}
};
template <System::Action A>
inline void System::action (TimeDisplay::Mode mode) {
SetTimeDisplay<A,0>::execute(mode); // Template recursion.
finalAction<A>();
}
template <>
inline void System::finalAction<System::DisplayTime>() {std::cout << "Current time: " << timeDisplay->tellMaybeWithPeriod() << '\n';}
template <>
inline void System::finalAction<System::Foo>() {std::cout << "Foo: " << timeDisplay->tellMaybeWithPeriod() << '\n';}
template <>
inline void System::finalAction<System::Bar>() {std::cout << "Bar: " << timeDisplay->tellMaybeWithPeriod() << '\n';}
template <>
inline void System::finalAction<System::Baz>() {std::cout << "Baz: " << timeDisplay->tellMaybeWithPeriod() << '\n';}
int main() {
for (int i = 0; i < TimeDisplay::NumTimeDiplayModes; i++)
System::action<System::DisplayTime>(static_cast<TimeDisplay::Mode>(i)); // System::displayCurrentTime is now just one of the specializations of System::action<A>. The other specializations are below.
std::cout << "-----------------------------------------------------------------------------\n";
for (int i = 0; i < TimeDisplay::NumTimeDiplayModes; i++)
System::action<System::Foo>(static_cast<TimeDisplay::Mode>(i));
std::cout << "-----------------------------------------------------------------------------\n";
for (int i = 0; i < TimeDisplay::NumTimeDiplayModes; i++)
System::action<System::Bar>(static_cast<TimeDisplay::Mode>(i));
std::cout << "-----------------------------------------------------------------------------\n";
for (int i = 0; i < TimeDisplay::NumTimeDiplayModes; i++)
System::action<System::Baz>(static_cast<TimeDisplay::Mode>(i));
}