I want to make factories that can chained together to create a pile of layers. One of the advantage of chained factories is that later factories can pass specifications to earlier factories. For example, if I want to make a tree on top of an array, I wouldn't know the correct size of the array until I know the size of the tree.
In the code I posted for review, the bottom layer of any pile must be an A (or any other class of memory layer in the actual library). A can only be the lowest layer in a pile.
Here is an example of chaining the factories.
// Create B above B above A auto bba = B_maker(9) % B_maker(13) % A_maker(17); A and B<...> both have move constructors.
I try to avoid writing each factory from scratch with CRTP.
The code is also at github.
Thanks.
Result:
ID: 2 Value: 9 Type: B<B<A> > ID: 1 Value: 13 Type: B<A> ID: 0 Value: 17 Type: A demo.cpp:
#include <iostream> #include <utility> #include "chained_factory.h" #include "test_classes.h" template<typename Prev_maker=Null_maker> class A_maker : public Foundation_factory<A_maker<Prev_maker>> { public: static constexpr bool is_base_maker = true; explicit A_maker(int val, Prev_maker&& prev = Null_maker()) : val_(val), prev_(std::move(prev)) {} template<typename T> auto rebind_prev(T&& prev) { return A_maker<T>(val_, std::forward<T&&>(prev)); } private: auto syn() { return A(val_); } Prev_maker prev_; int val_; friend Foundation_factory<A_maker<Prev_maker>>; }; template<typename Prev_maker=Null_maker> class B_maker : public Chaining_factory<B_maker<Prev_maker>> { using self_type = B_maker<Prev_maker>; public: explicit B_maker(int val, Prev_maker&& prev = Null_maker()) : val_(val), prev_(std::move(prev)) {} template<typename T> auto rebind_prev(T&& prev) { auto r = B_maker<T>(val_, std::forward<T&&>(prev)); return r; } private: template <typename T> auto syn(T&& from_next) { return B(std::forward<T&&>(from_next), val_); } Prev_maker prev_; int val_; friend Prev_maker; friend Chaining_factory<B_maker<Prev_maker>>; }; int main() { auto bba = B_maker(9) % B_maker(13) % A_maker(17); std::cout << bba; return 0; } chain_factory.h:
#ifndef CHAINED_FACTORY_CHAINED_FACTORY_H #define CHAINED_FACTORY_CHAINED_FACTORY_H #include <iostream> struct Null { }; class Null_maker { public: template <typename T> auto make(T&& x) { return std::forward<T&&>(x); } using target_type = Null; }; template<typename Derived> class Chaining_factory { public: static constexpr bool is_base_maker = false; template<typename T> auto make(T&& from_next) { return derived()->prev_.make(derived()->syn(std::forward<T&&>(from_next))); } template<typename T> auto operator%(T&& other_maker) { auto result = other_maker.template rebind_prev<Derived>( std::move(*derived())); if constexpr (T::is_base_maker) { return result.make(); } else { return result; } } private: auto derived() { return static_cast<Derived*>(this); } }; template<typename Derived> class Foundation_factory { public: static constexpr bool is_base_maker = true; auto make() { return derived()->prev_.make(derived()->syn()); } private: auto derived() { return static_cast<Derived*>(this); } }; #endif //CHAINED_FACTORY_CHAINED_FACTORY_H test_class.h:
#ifndef CHAINED_FACTORY_TEST_CLASSES_H #define CHAINED_FACTORY_TEST_CLASSES_H #include "type2name.h" struct A { static constexpr int id = 0; explicit A(int val) : val_(val) {} int val_{-1}; }; template<typename P=A> struct B { static constexpr int id = 1 + P::id; B(P&& p, int val) : p_(std::move(p)), val_(val) {} P p_; int val_{-1}; }; std::ostream& operator<<(std::ostream& os, const A& m) { return os << "ID: " << m.id << " " << "Value: " << m.val_ << " " << "Type: " << type2name<A>() << "\n"; } template<typename P> std::ostream& operator<<(std::ostream& os, const B<P>& m) { return os << "ID: " << m.id << " " << "Value: " << m.val_ << " " << "Type: " << type2name<B<P>>() << "\n" << m.p_; } #endif //CHAINED_FACTORY_TEST_CLASSES_H
make_pile(A_maker(2, 3), B_maker(3, 4), ...)and each maker would not track earlier makers. But that means passing requirements between makers need a stream interface, and buffer. That means the compiler have 0 chance to optimize away the construction by the factories. \$\endgroup\$