The C++ Template Interface Pattern (TIP)
The C++ Template Interface Pattern (TIP) is the application of the Curiously Recurring Template Pattern (CRTP) to a set of one or more public “interface” classes nested inside a public “set of interfaces” class where the “set of interfaces” class uses template template parameters to pass in “implementation” classes.
The advantages of using the C++ Template Interface Pattern (TIP) which leverages compile-time indirection over the traditional C++ interface pattern that uses run-time indirection (i.e. inheriting from classes that only contain pure virtual functions and indirecting through a pointer to a table of virtual function pointers at run-time to perform member function calls) is that:
- For optimized builds (i.e. “Release” builds), programs using the C++ Template Interface Pattern (TIP) are faster than programs using the traditional C++ interface pattern. Code using the TIP is optimized down to fewer assembly language instructions than code using the traditional interface pattern due to the relative ease of optimizing the compile-time indirection used by the TIP compared to the run-time indirection used in the traditional case.
- Classes using the C++ Template Interface Pattern (TIP) are smaller than their traditional C++ interface pattern equivalents. TIP classes do not contain a pointer to a table of virtual function pointers; therefore, TIP classes are consistently eight bytes smaller for x64 builds and four bytes smaller for x86 builds.
- If Run-Time Polymorphism is needed, it is trivial to add the traditional C++ interface pattern on top of the C++ Template Interface Pattern (TIP); however, the size advantage from the previous point will be lost. In other words, TIP and traditional interfaces are compatible.
Here’s a quick example of the C++ Template Interface Pattern (TIP) to implement a trivial version of the Layers Architectural Pattern:
- Compiler Explorer: https://godbolt.org/z/WJseMT
- Wandbox: https://wandbox.org/permlink/0Y9PJ6TAXu13GWrq
#include <iostream> #include <memory> #ifdef __FUNCSIG__ #define PRINT_FUNCTION std::wcout << __FUNCSIG__ << '\n' #else #define PRINT_FUNCTION std::wcout << __func__ << '\n' #endif template < template <typename, typename, typename> typename Layer1Template, template <typename, typename, typename> typename Layer2Template, template <typename, typename, typename> typename Layer3Template > class Layers // A set of related interfaces { public: class ILayer1; class ILayer2; class ILayer3; private: using ILayer1LowerLayer = std::nullptr_t; using ILayer1HigherLayer = ILayer2; using ILayer2LowerLayer = ILayer1; using ILayer2HigherLayer = ILayer3; using ILayer3LowerLayer = ILayer2; using ILayer3HigherLayer = std::nullptr_t; using Layer1 = Layer1Template<ILayer1, ILayer1LowerLayer, ILayer1HigherLayer>; // CRTP using Layer2 = Layer2Template<ILayer2, ILayer2LowerLayer, ILayer2HigherLayer>; // CRTP using Layer3 = Layer3Template<ILayer3, ILayer3LowerLayer, ILayer3HigherLayer>; // CRTP public: class ILayer1 : public Layer1 { private: using This = Layer1; using LowerLayer = ILayer1LowerLayer; using HigherLayer = ILayer1HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void TransitionFromDownToUp() noexcept { return This::TransitionFromDownToUp(); } constexpr void Up() noexcept { return This::Up(); } }; class ILayer2 : public Layer2 { private: using This = Layer2; using LowerLayer = ILayer2LowerLayer; using HigherLayer = ILayer2HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void Up() noexcept { return This::Up(); } constexpr int Value() const noexcept { return This::Value(); } constexpr void Value(int value) noexcept { return This::Value(value); } }; class ILayer3 : public Layer3 { private: using This = Layer3; using LowerLayer = ILayer3LowerLayer; using HigherLayer = ILayer3HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void Up() noexcept { return This::Up(); } constexpr int GetLayer2Value() const noexcept { return This::GetLayer2Value(); } constexpr void SetLayer2Value(int value) noexcept { return This::SetLayer2Value(value); } }; }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer1 { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; public: Layer1() = default; Layer1(const Layer1&) = delete; Layer1(Layer1&&) = delete; ~Layer1() = default; Layer1& operator =(const Layer1&) = delete; Layer1& operator =(Layer1&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; TransitionFromDownToUp(); } constexpr void TransitionFromDownToUp() noexcept { PRINT_FUNCTION; Up(); } constexpr void Up() noexcept { PRINT_FUNCTION; m_higher_layer_ptr->Up(); } }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer2 { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; int m_value{ 123 }; public: Layer2() = default; Layer2(const Layer2&) = delete; Layer2(Layer2&&) = delete; ~Layer2() = default; Layer2& operator =(const Layer2&) = delete; Layer2& operator =(Layer2&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; m_lower_layer_ptr->Down(); } constexpr void Up() noexcept { PRINT_FUNCTION; m_higher_layer_ptr->Up(); } constexpr int Value() const noexcept { //PRINT_FUNCTION; return m_value; } constexpr void Value(int value) noexcept { //PRINT_FUNCTION; m_value = value; } }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer3 { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; public: Layer3() = default; Layer3(const Layer3&) = delete; Layer3(Layer3&&) = delete; ~Layer3() = default; Layer3& operator =(const Layer3&) = delete; Layer3& operator =(Layer3&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; m_lower_layer_ptr->Down(); } constexpr void Up() noexcept { PRINT_FUNCTION; } constexpr int GetLayer2Value() const noexcept { //PRINT_FUNCTION; return m_lower_layer_ptr->Value(); } constexpr void SetLayer2Value(int value) noexcept { //PRINT_FUNCTION; return m_lower_layer_ptr->Value(value); } }; int main() { using ILayers = Layers<Layer1, Layer2, Layer3>; auto layer1_ptr{ std::make_unique<ILayers::ILayer1>() }; auto layer2_ptr{ std::make_unique<ILayers::ILayer2>() }; auto layer3_ptr{ std::make_unique<ILayers::ILayer3>() }; layer1_ptr->SetLowerLayer(nullptr); layer1_ptr->SetHigherLayer(layer2_ptr.get()); layer2_ptr->SetLowerLayer(layer1_ptr.get()); layer2_ptr->SetHigherLayer(layer3_ptr.get()); layer3_ptr->SetLowerLayer(layer2_ptr.get()); layer3_ptr->SetHigherLayer(nullptr); layer3_ptr->Down(); std::wcout << layer3_ptr->GetLayer2Value() << '\n'; int value{ 0 }; std::wcin >> value; layer3_ptr->SetLayer2Value(value); std::wcout << layer3_ptr->GetLayer2Value() << '\n'; }
When the code above is compiled with g++ and run, it produces the following output (assuming an input of “456”):
Down
Down
Down
TransitionFromDownToUp
Up
Up
Up
123
456
456
Here’s that example of the C++ Template Interface Pattern (TIP) again combined with the traditional C++ interface pattern to produced a section of code using Run-Time Polymorphism:
- Compiler Explorer: https://godbolt.org/z/IR94dh
- Wandbox: https://wandbox.org/permlink/uyANK7sOh6cGjAtW
#include <iostream> #include <memory> #include <vector> #ifdef __FUNCSIG__ #define PRINT_FUNCTION std::wcout << __FUNCSIG__ << '\n' #else #define PRINT_FUNCTION std::wcout << __func__ << '\n' #endif template < template <typename, typename, typename> typename Layer1Template, template <typename, typename, typename> typename Layer2Template, template <typename, typename, typename> typename Layer3Template > class Layers // A set of related interfaces { public: class ILayer1; class ILayer2; class ILayer3; private: using ILayer1LowerLayer = std::nullptr_t; using ILayer1HigherLayer = ILayer2; using ILayer2LowerLayer = ILayer1; using ILayer2HigherLayer = ILayer3; using ILayer3LowerLayer = ILayer2; using ILayer3HigherLayer = std::nullptr_t; using Layer1 = Layer1Template<ILayer1, ILayer1LowerLayer, ILayer1HigherLayer>; // CRTP using Layer2 = Layer2Template<ILayer2, ILayer2LowerLayer, ILayer2HigherLayer>; // CRTP using Layer3 = Layer3Template<ILayer3, ILayer3LowerLayer, ILayer3HigherLayer>; // CRTP public: class ILayer1 : public Layer1 { private: using This = Layer1; using LowerLayer = ILayer1LowerLayer; using HigherLayer = ILayer1HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void TransitionFromDownToUp() noexcept { return This::TransitionFromDownToUp(); } constexpr void Up() noexcept { return This::Up(); } }; class ILayer2 : public Layer2 { private: using This = Layer2; using LowerLayer = ILayer2LowerLayer; using HigherLayer = ILayer2HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void Up() noexcept { return This::Up(); } constexpr int Value() const noexcept { return This::Value(); } constexpr void Value(int value) noexcept { return This::Value(value); } }; class ILayer3 : public Layer3 { private: using This = Layer3; using LowerLayer = ILayer3LowerLayer; using HigherLayer = ILayer3HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void Up() noexcept { return This::Up(); } constexpr int GetLayer2Value() const noexcept { return This::GetLayer2Value(); } constexpr void SetLayer2Value(int value) noexcept { return This::SetLayer2Value(value); } }; }; struct IForRuntimePolymorphism { virtual ~IForRuntimePolymorphism() = default; virtual void DoSomething() noexcept = 0; }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer1 : public IForRuntimePolymorphism { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; public: Layer1() = default; Layer1(const Layer1&) = delete; Layer1(Layer1&&) = delete; ~Layer1() = default; Layer1& operator =(const Layer1&) = delete; Layer1& operator =(Layer1&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; TransitionFromDownToUp(); } constexpr void TransitionFromDownToUp() noexcept { PRINT_FUNCTION; Up(); } constexpr void Up() noexcept { PRINT_FUNCTION; m_higher_layer_ptr->Up(); } virtual void DoSomething() noexcept override { PRINT_FUNCTION; } }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer2 : public IForRuntimePolymorphism { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; int m_value{ 123 }; public: Layer2() = default; Layer2(const Layer2&) = delete; Layer2(Layer2&&) = delete; ~Layer2() = default; Layer2& operator =(const Layer2&) = delete; Layer2& operator =(Layer2&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; m_lower_layer_ptr->Down(); } constexpr void Up() noexcept { PRINT_FUNCTION; m_higher_layer_ptr->Up(); } constexpr int Value() const noexcept { //PRINT_FUNCTION; return m_value; } constexpr void Value(int value) noexcept { //PRINT_FUNCTION; m_value = value; } virtual void DoSomething() noexcept override { PRINT_FUNCTION; } }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer3 : public IForRuntimePolymorphism { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; public: Layer3() = default; Layer3(const Layer3&) = delete; Layer3(Layer3&&) = delete; ~Layer3() = default; Layer3& operator =(const Layer3&) = delete; Layer3& operator =(Layer3&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; m_lower_layer_ptr->Down(); } constexpr void Up() noexcept { PRINT_FUNCTION; } constexpr int GetLayer2Value() const noexcept { //PRINT_FUNCTION; return m_lower_layer_ptr->Value(); } constexpr void SetLayer2Value(int value) noexcept { //PRINT_FUNCTION; return m_lower_layer_ptr->Value(value); } virtual void DoSomething() noexcept override { PRINT_FUNCTION; } }; int main() { using ILayers = Layers<Layer1, Layer2, Layer3>; auto layer1_ptr{ std::make_unique<ILayers::ILayer1>() }; auto layer2_ptr{ std::make_unique<ILayers::ILayer2>() }; auto layer3_ptr{ std::make_unique<ILayers::ILayer3>() }; layer1_ptr->SetLowerLayer(nullptr); layer1_ptr->SetHigherLayer(layer2_ptr.get()); layer2_ptr->SetLowerLayer(layer1_ptr.get()); layer2_ptr->SetHigherLayer(layer3_ptr.get()); layer3_ptr->SetLowerLayer(layer2_ptr.get()); layer3_ptr->SetHigherLayer(nullptr); layer3_ptr->Down(); std::wcout << layer3_ptr->GetLayer2Value() << '\n'; int value{ 0 }; std::wcin >> value; layer3_ptr->SetLayer2Value(value); std::wcout << layer3_ptr->GetLayer2Value() << '\n'; std::vector<std::unique_ptr<IForRuntimePolymorphism>> layer_ptrs{}; layer_ptrs.emplace_back(std::move(layer1_ptr)); layer_ptrs.emplace_back(std::move(layer2_ptr)); layer_ptrs.emplace_back(std::move(layer3_ptr)); for (auto&& layer_ptr : layer_ptrs) { layer_ptr->DoSomething(); } }
When the code above is compiled with g++ and run, it produces the following output (assuming an input of “456”):
Down
Down
Down
TransitionFromDownToUp
Up
Up
Up
123
456
456
DoSomething
DoSomething
DoSomething
Of course, if you’re able to leverage C++ 2017 (a.k.a. C++17) with its std::variant and std::visit, then it’s fairly easy to switch from Run-Time Polymorphism to Compile-Time Polymorphism:
- Compiler Explorer: https://godbolt.org/z/1jEeDU
- Wandbox: https://wandbox.org/permlink/gnWGQRscUZeNxo91
#include <iostream> #include <memory> #include <vector> #include <variant> #include <type_traits> #ifdef __FUNCSIG__ #define PRINT_FUNCTION std::wcout << __FUNCSIG__ << '\n' #else #define PRINT_FUNCTION std::wcout << __func__ << '\n' #endif template < template <typename, typename, typename> typename Layer1Template, template <typename, typename, typename> typename Layer2Template, template <typename, typename, typename> typename Layer3Template > class Layers // A set of related interfaces { public: class ILayer1; class ILayer2; class ILayer3; private: using ILayer1LowerLayer = std::nullptr_t; using ILayer1HigherLayer = ILayer2; using ILayer2LowerLayer = ILayer1; using ILayer2HigherLayer = ILayer3; using ILayer3LowerLayer = ILayer2; using ILayer3HigherLayer = std::nullptr_t; using Layer1 = Layer1Template<ILayer1, ILayer1LowerLayer, ILayer1HigherLayer>; // CRTP using Layer2 = Layer2Template<ILayer2, ILayer2LowerLayer, ILayer2HigherLayer>; // CRTP using Layer3 = Layer3Template<ILayer3, ILayer3LowerLayer, ILayer3HigherLayer>; // CRTP public: class ILayer1 : public Layer1 { private: using This = Layer1; using LowerLayer = ILayer1LowerLayer; using HigherLayer = ILayer1HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void TransitionFromDownToUp() noexcept { return This::TransitionFromDownToUp(); } constexpr void Up() noexcept { return This::Up(); } constexpr void DoSomething() noexcept { return This::DoSomething(); } }; class ILayer2 : public Layer2 { private: using This = Layer2; using LowerLayer = ILayer2LowerLayer; using HigherLayer = ILayer2HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void Up() noexcept { return This::Up(); } constexpr int Value() const noexcept { return This::Value(); } constexpr void Value(int value) noexcept { return This::Value(value); } constexpr void DoSomething() noexcept { return This::DoSomething(); } }; class ILayer3 : public Layer3 { private: using This = Layer3; using LowerLayer = ILayer3LowerLayer; using HigherLayer = ILayer3HigherLayer; public: constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { return This::SetLowerLayer(lower_layer_ptr); } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { return This::SetHigherLayer(higher_layer_ptr); } constexpr void Down() noexcept { return This::Down(); } constexpr void Up() noexcept { return This::Up(); } constexpr int GetLayer2Value() const noexcept { return This::GetLayer2Value(); } constexpr void SetLayer2Value(int value) noexcept { return This::SetLayer2Value(value); } constexpr void DoSomething() noexcept { return This::DoSomething(); } }; }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer1 { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; public: Layer1() = default; Layer1(const Layer1&) = delete; Layer1(Layer1&&) = delete; ~Layer1() = default; Layer1& operator =(const Layer1&) = delete; Layer1& operator =(Layer1&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; TransitionFromDownToUp(); } constexpr void TransitionFromDownToUp() noexcept { PRINT_FUNCTION; Up(); } constexpr void Up() noexcept { PRINT_FUNCTION; m_higher_layer_ptr->Up(); } constexpr void DoSomething() noexcept { PRINT_FUNCTION; } }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer2 { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; int m_value{ 123 }; public: Layer2() = default; Layer2(const Layer2&) = delete; Layer2(Layer2&&) = delete; ~Layer2() = default; Layer2& operator =(const Layer2&) = delete; Layer2& operator =(Layer2&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; m_lower_layer_ptr->Down(); } constexpr void Up() noexcept { PRINT_FUNCTION; m_higher_layer_ptr->Up(); } constexpr int Value() const noexcept { //PRINT_FUNCTION; return m_value; } constexpr void Value(int value) noexcept { //PRINT_FUNCTION; m_value = value; } constexpr void DoSomething() noexcept { PRINT_FUNCTION; } }; template <typename This, typename LowerLayer, typename HigherLayer> class Layer3 { private: LowerLayer* m_lower_layer_ptr{ nullptr }; HigherLayer* m_higher_layer_ptr{ nullptr }; public: Layer3() = default; Layer3(const Layer3&) = delete; Layer3(Layer3&&) = delete; ~Layer3() = default; Layer3& operator =(const Layer3&) = delete; Layer3& operator =(Layer3&&) = delete; constexpr void SetLowerLayer(LowerLayer* lower_layer_ptr) noexcept { m_lower_layer_ptr = lower_layer_ptr; } constexpr void SetHigherLayer(HigherLayer* higher_layer_ptr) noexcept { m_higher_layer_ptr = higher_layer_ptr; } constexpr void Down() noexcept { PRINT_FUNCTION; m_lower_layer_ptr->Down(); } constexpr void Up() noexcept { PRINT_FUNCTION; } constexpr int GetLayer2Value() const noexcept { //PRINT_FUNCTION; return m_lower_layer_ptr->Value(); } constexpr void SetLayer2Value(int value) noexcept { //PRINT_FUNCTION; return m_lower_layer_ptr->Value(value); } constexpr void DoSomething() noexcept { PRINT_FUNCTION; } }; int main() { using ILayers = Layers<Layer1, Layer2, Layer3>; auto layer1_ptr{ std::make_unique<ILayers::ILayer1>() }; auto layer2_ptr{ std::make_unique<ILayers::ILayer2>() }; auto layer3_ptr{ std::make_unique<ILayers::ILayer3>() }; layer1_ptr->SetLowerLayer(nullptr); layer1_ptr->SetHigherLayer(layer2_ptr.get()); layer2_ptr->SetLowerLayer(layer1_ptr.get()); layer2_ptr->SetHigherLayer(layer3_ptr.get()); layer3_ptr->SetLowerLayer(layer2_ptr.get()); layer3_ptr->SetHigherLayer(nullptr); layer3_ptr->Down(); std::wcout << layer3_ptr->GetLayer2Value() << '\n'; int value{ 0 }; std::wcin >> value; layer3_ptr->SetLayer2Value(value); std::wcout << layer3_ptr->GetLayer2Value() << '\n'; using Layer1PtrType = std::decay_t<decltype(layer1_ptr)>; using Layer2PtrType = std::decay_t<decltype(layer2_ptr)>; using Layer3PtrType = std::decay_t<decltype(layer3_ptr)>; using LayerPtrVariantType = std::variant < Layer1PtrType, Layer2PtrType, Layer3PtrType >; std::vector<LayerPtrVariantType> layer_ptr_variants{}; layer_ptr_variants.emplace_back(std::move(layer1_ptr)); layer_ptr_variants.emplace_back(std::move(layer2_ptr)); layer_ptr_variants.emplace_back(std::move(layer3_ptr)); for (auto&& layer_ptr_variant : layer_ptr_variants) { std::visit ( [](auto&& layer_ptr) noexcept { layer_ptr->DoSomething(); }, layer_ptr_variant ); } }
When the code above is compiled with g++ and run, it produces the following output (assuming an input of “456”):
Down
Down
Down
TransitionFromDownToUp
Up
Up
Up
123
456
456
DoSomething
DoSomething
DoSomething
- Build and static link ZeroMQ on Windows
- Three-Dimensional Givens Rotations