#include <iostream>
// custom struct to ensure functions are called in the correct order
template<int N>
struct Precedence : public Precedence<N - 1> {};
template<>
struct Precedence<0> {};
// The main printer that delegates function calls of other classes.
// Its 'print' function accepts classes that have defined:
// - void print_time(int mins, double secs)
// - void print_time(double total_secs)
// - void print_time(int mins)
// or none of the above.
class PrinterDelegator {
public:
// The Precedence variable ensures the function with the highest 'N' value
// gets called. If the function with the current 'N' value is ill-formed,
// the Precedence struct will cast to its next highest base class until
// a properly formed function is available.
template<typename T>
void print(const T &printer) {
print(printer, p_);
}
private:
// these would all actually be incrementing
int mins_{3};
double secs_{22.5};
double total_secs_{202.5};
Precedence<3> p_{};
// The "best" function. Call this if available
template<typename T>
auto print(const T &printer, const Precedence<3>&) -> decltype(printer.print_time(mins_, secs_)) {
printer.print_time(mins_, secs_);
}
// Other available functions ordered by importance
template<typename T>
auto print(const T &printer, const Precedence<2>&) -> decltype(printer.print_time(total_secs_)) {
printer.print_time(total_secs_);
}
template<typename T>
auto print(const T &printer, const Precedence<1>&) -> decltype(printer.print_time(mins_)) {
printer.print_time(mins_);
}
// default empty definition allowing for classes that haven't defined 'print_time'
template<typename T>
auto print(const T &printer, const Precedence<0>&) -> decltype(void()) {
std::cout << "nothing" << std::endl;
}
};
// class with all possible functions. Only the "best"
// one will get called: 'void print_time(int, double)'
class VerbosePrinter {
public:
void print_time(int mins, double secs) const {
print_time(mins);
print_time(secs);
}
void print_time(double total_secs) const {
std::cout << total_secs << "s" << std::endl;
}
void print_time(int mins) const {
std::cout << mins << "m" << std::endl;
}
};
// Class with two of the function options. The best one here is
// 'void print_time(double)' so that one will be called
class SinglePrinter {
public:
void print_time(double total_secs) const {
std::cout << total_secs << "s" << std::endl;
}
void print_time(int mins) const {
std::cout << mins << "m" << std::endl;
}
};
// Empty class that can still be used even though no functions are defined
class EmptyPrinter {};
int main() {
PrinterDelegator pd;
VerbosePrinter vp;
std::cout << "Should print minutes and seconds:" << std::endl;
pd.print(vp);
SinglePrinter sp;
std::cout << "Should print total seconds:" << std::endl;
pd.print(sp);
EmptyPrinter ep;
std::cout << "Should print nothing:" << std::endl;
pd.print(ep);
return 0;
}