typedef TypeList<int,char,bool,string, EmptyList> MyTypeList.
typedef TypeList<int,char,bool,string, EmptyList> MyTypeList;
typedef TypeList<int,TypeList<char,TypeList<bool,TypeList<string, EmptyList>>>> MyTypeList;
struct EmptyList{}; template<typename Head, typename... Tail> struct TypeList { typedef Head head_t; typedef TypeList<Tail...> tail_t; }; template<typename Head> struct TypeList<Head, EmptyList> { typedef Head head_t; typedef EmptyList tail_t; };
-------------------------------- typelists.h -------------------------------- #pragma once struct EmptyList{}; template<typename Head, typename... Tail> struct TypeList { typedef Head head_t; typedef TypeList<Tail...> tail_t; }; template<typename Head> struct TypeList<Head, EmptyList> { typedef Head head_t; typedef EmptyList tail_t; }; typedef TypeList<int, double, bool, char, const char*, EmptyList> MyTypeList; ..... , ... -------------------------------- myclass.h -------------------------------- #pragma once class MyClass { public: template<typename T> void f(T x); }; -------------------------------- myclass.cpp -------------------------------- #include "templateclass.h" #include "typelist.h" #include <iostream> namespace { InstantiateMemFunc(MyClass, f, MyTypeList) // (*) } template<typename T> void MyClass::f(T x) { std::cout<< x << "\n"; } -------------------------------- main.cpp -------------------------------- #include <typelist.h> #include "myclass.h" int main() { MyClass tc; tc.f(3); tc.f(2.0); tc.f(true); tc.f('a'); tc.f("hello world"); return 0; }
template<> void MyClass::f<int>(int); template<> void MyClass::f<double>(double); template<> void MyClass::f<bool>(bool); template<> void MyClass::f<char>(char); template<> void MyClass::f<const char*>(const char*);
InstantiateMemberFunction
expression in the file myclass.cpp. template<typename Types> struct SomethingDoer; // , - template<> struct SomethingDoer<EmptyList> // { static void doSomething(...) // - , . {} }; template<typename Head, typename... Tail> struct SomethingDoer<TypeList<Head, Tail...> > // . { static void doSomething() { ... - - Head .... (**) SomethingDoer<typename TypeList<Head, Tail...>::tail_t>::doSomething(); // } };
template <typename T> void f() {...} template <typename T> void g(T t) {...} struct S { template <typename T> void memFunc(){...} }; f<int>(); // g(58); // void (*gPtr)(int) = g; // void (S::*memFuncPtr)() = &S::memFunc<- , , int>; //
template<typename Head, typename... Tail> struct SomethingDoer<TypeList<Head, Tail...> > { typedef void (MyClass::*MemFuncType)(Head); static void doSomething() { MemFuncType x = &MyClass::f; (void)(x); // , SomethingDoer<typename TypeList<Head, Tail...>::tail_t>::doSomething(); } };
SomethingDoer<MyTypeList>::instantiate();
struct Instantiator { Instantiator() { SomethingDoer<MyTypeList>::instantiate(); } };
x
from the doSomething
method and the constructor of the Instantiator
class. But it's not a problem. You just need to convince the compiler that these two things are still very important. With
- it's simple. You can, for example, declare a variable volatile, and let only the compiler dare to do something with it. And with the constructor Instantiator
- take and declare a variable of type Instantiator
. #include "myclass.h" #include "typelist.h" #include <iostream> namespace { template<typename Types> struct SomethingDoer; template<> struct SomethingDoer<EmptyList> { static void doSomething(...) {} }; template<typename Head, typename... Tail> struct SomethingDoer<TypeList<Head, Tail...> > { typedef void (MyClass::*MemFuncType)(Head); static void doSomething() { volatile MemFuncType x = &MyClass::f; (void)(x); SomethingDoer<typename TypeList<Head, Tail...>::tail_t>::doSomething(); } }; template <typename TList> struct Instantiator { Instantiator() { SomethingDoer<TList>::doSomething(); } }; Instantiator<MyTypeList> a; // . } template<typename T> void MyClass::f(T x) { std::cout<< x << "\n"; }
// myclass.h #pragma once class MyClass { public: template<typename T> void f(T x); }; // myclass.cpp #include "templateclass.h" #include "typelist.h" #include <iostream> namespace { template<typename Types> struct SomethingDoer; template<> struct SomethingDoer<EmptyList> { static void doSomething(...) {} }; template<typename Head, typename... Tail> struct SomethingDoer<TypeList<Head, Tail...> > { typedef void (MyClass::*MemFuncType)(Head); static void doSomething() { volatile MemFuncType x = &MyClass::f; (void)(x); SomethingDoer<typename TypeList<Head, Tail...>::tail_t>::doSomething(); } }; template <typename TList> struct Instantiator { Instantiator() { SomethingDoer<TList>::doSomething(); } }; Instantiator<MyTypeList> a; // . } template<typename T> void MyClass::f(T x) { std::cout<< x << "\n"; } // typelists.h #pragma once struct EmptyList{}; template<typename Head, typename... Tail> struct TypeList { typedef Head head_t; typedef TypeList<Tail...> tail_t; }; template<typename Head> struct TypeList<Head, EmptyList> { typedef Head head_t; typedef EmptyList tail_t; }; typedef TypeList<int, double, bool, char, const char*, EmptyList> MyTypeList; // main.cpp #include <typelist.h> #include "myclass.h" int main() { MyClass tc; tc.f(3); tc.f(2.0); tc.f(true); tc.f('a'); tc.f("hello world"); return 0; }
doSomething
method, although this is the least of all the evils. The biggest evil is that each user will have to understand how this is done, because the code of the instantiation mechanism and the launch code of this mechanism are closely intertwined. It would be nice to hide the mechanism, no matter how cumbersome and confusing it might be. Yes, so hide, so that the user was enough to write: InstantiateMemFunc(MyClass, f, MyTypeList) // (*)
Source: https://habr.com/ru/post/235831/
All Articles