Motivation: without SaveDecorator
, we would have to write:
void Person::save (std::ostream& os) const {
os << tag << '\n';
saveParticular(os);
}
void Worker::save (std::ostream& os) const {
os << tag << '\n';
Person::saveParticular(os);
saveParticular(os);
}
void Teacher::save (std::ostream& os) const {
os << tag << '\n';
Person::saveParticular(os);
Worker::saveParticular(os);
saveParticular(os);
}
and it is easy to forget to always add the line os << tag << '\n';
at the beginning for new derived types of Person
. So SaveDecorator
will decorate the save
member functions, which is appearing as virtual overrides in all the derived types of Person
, as follows:
#include <iostream>
template <typename T> // Could not seem to apply pack expansion with the syntax 'void(T::*)(std::ostream&) const... fs', so this alias is apparently needed.
using memberf = void(T::*)(std::ostream&) const;
template <typename...> struct SaveDecorator;
template <>
struct SaveDecorator<> {
template <typename T> void execute (const T*, std::ostream&) const {} // End of recursion.
};
template <typename T, typename... Ts>
class SaveDecorator<T, Ts...> : public SaveDecorator<Ts...> {
void(T::*func)(std::ostream&) const;
public:
SaveDecorator (void(T::*f)(std::ostream&) const, memberf<Ts>... fs) : SaveDecorator<Ts...>(fs...), func(f) { }
void operator()(const T* t, std::ostream& os) const { os << T::tag << '\n'; execute(t, os); }
protected:
void execute (const T* t, std::ostream& os) const {
SaveDecorator<Ts...>::execute(t, os); // More decorated lines to func that are added in the middle.
(t->*func)(os);
}
};
template <typename T, typename... Ts>
SaveDecorator<T, Ts...> makeSaveDecorator() {
return SaveDecorator<T, Ts...>(&T::saveParticular, &Ts::saveParticular...);
}
class Person {
std::string name;
public:
static const std::string tag;
virtual ~Person() = default;
Person (const std::string& n) : name(n) { }
virtual void save (std::ostream& os) const { makeSaveDecorator<Person>()(this, os); }
void saveParticular (std::ostream& os) const { os << name << '\n'; }
};
const std::string Person::tag = "Person";
class Worker : public Person {
int ID;
public:
static const std::string tag;
Worker (const std::string& name, int i) : Person(name), ID(i) { }
virtual void save (std::ostream& os) const override { makeSaveDecorator<Worker, Person>()(this, os); }
void saveParticular (std::ostream& os) const { os << ID << '\n'; }
};
const std::string Worker::tag = "Worker";
class Teacher : public Worker {
std::string school;
public:
static const std::string tag;
Teacher (const std::string& name, int ID, const std::string& s) : Worker(name, ID), school(s) { }
virtual void save (std::ostream& os) const override { makeSaveDecorator<Teacher, Worker, Person>()(this, os); }
void saveParticular (std::ostream& os) const { os << school << '\n'; }
};
const std::string Teacher::tag = "Teacher";
int main() {
Person person("Mike");
Worker worker("Sam", 25);
Teacher teacher("Mary", 45, "Northern CI");
person.save(std::cout);
worker.save(std::cout);
teacher.save(std::cout);
}
Output:
Person
Mike
Worker
Sam
25
Teacher
Mary
45
Northern CI
Here is the decorator class rewritten as generically as I could:
#include <iostream>
template <typename R, typename T, typename... Args> // Could not seem to apply pack expansion with the syntax 'R(Ts::*)(Args...) const... fs', so this alias is apparently needed.
using memberf = R(T::*)(Args...) const;
template <typename...> struct MemberFunctionDecorator;
template <template <typename, typename...> class Pack, typename R, typename... Args>
struct MemberFunctionDecorator<Pack<R, Args...>> {
MemberFunctionDecorator (R(*)(Args...)) { }
template <typename T> R execute (const T*, Args&&...) const {} // End of recursion.
};
template <template <typename, typename...> class Pack, typename R, typename... Args, typename T, typename... Ts>
class MemberFunctionDecorator<Pack<R, Args...>, T, Ts...> : public MemberFunctionDecorator<Pack<R, Args...>, Ts...> {
R(*argFunc)(Args...);
R(T::*func)(Args...) const;
public:
MemberFunctionDecorator (R(*af)(Args...), R(T::*f)(Args...) const, memberf<R, Ts, Args...>... fs) : MemberFunctionDecorator<Pack<R, Args...>, Ts...>(af, fs...), argFunc(af), func(f) { }
R operator()(const T* t, Args&&... args) const { (*argFunc)(std::forward<Args>(args)...); return execute(t, std::forward<Args>(args)...); }
protected:
R execute (const T* t, Args&&... args) const {
MemberFunctionDecorator<Pack<R, Args...>, Ts...>::execute(t, std::forward<Args>(args)...); // More decorated lines to func that are added in the middle.
return (t->*func)(std::forward<Args>(args)...);
}
};
// Now illustrating the usage of MemberFunctionDecorator.
template <typename ReturnType, typename... Args> struct ArgPack;
template <typename T> void f(std::ostream& os) { os << T::tag << '\n'; }
template <typename T, typename... Ts>
MemberFunctionDecorator<ArgPack<void, std::ostream&>, T, Ts...> saveDecorator() { // Now we pass the particular member functions, including the std::ostream& argument and void return type.
return MemberFunctionDecorator<ArgPack<void, std::ostream&>, T, Ts...>(&f<T>, &T::saveParticular, &Ts::saveParticular...); // Cannot pass a (template) lambda function into the first argument.
}
class Person {
std::string name;
public:
static const std::string tag;
virtual ~Person() = default;
Person (const std::string& n) : name(n) { }
virtual void save (std::ostream& os) const { saveDecorator<Person>()(this, os); }
void saveParticular (std::ostream& os) const { os << name << '\n'; }
};
const std::string Person::tag = "Person";
class Worker : public Person {
int ID;
public:
static const std::string tag;
Worker (const std::string& name, int i) : Person(name), ID(i) { }
virtual void save (std::ostream& os) const override { saveDecorator<Worker, Person>()(this, os); }
void saveParticular (std::ostream& os) const { os << ID << '\n'; }
};
const std::string Worker::tag = "Worker";
class Teacher : public Worker {
std::string school;
public:
static const std::string tag;
Teacher (const std::string& name, int ID, const std::string& s) : Worker(name, ID), school(s) { }
virtual void save (std::ostream& os) const override { saveDecorator<Teacher, Worker, Person>()(this, os); }
void saveParticular (std::ostream& os) const { os << school << '\n'; }
};
const std::string Teacher::tag = "Teacher";
int main() {
Person person("Mike");
Worker worker("Sam", 25);
Teacher teacher("Mary", 45, "Northern CI");
person.save(std::cout);
worker.save(std::cout);
teacher.save(std::cout);
}