C++ MetaProgramming
前言
Note From:CppCon 2015: Fedor Pikus “C++ Metaprogramming: Journey from simple to insanity and back
MetaProgramming can be used in :
- calculation during compiling
- code genereation to boost runtime
- self-adaptive code
- type check and cast
- Type Dependent Code
- SFINAE-Selective Compile
一、Basic On Template
- Specialization and instantization
特化分为显示特化和实例化,实例化又分为显示实例化和隐式实例化。 - Template explicit instantization
//函数
template int func<int>(int);
//类
template class Array<char>;
//成员函数
template class Array<char>::getSize();
- 模板显示特化(具体化):
//函数
template<> int func<int>(int){...};
//类
template <> class Tuple<int, double>{...};
- 模板类外定义:
//成员函数
template<typename T>
Array<T>& Array<char>::operator+(const Array<char>& arr){...};
//非const的static变量
template<typename T>
int Array<T>::sharedInstance= 0; //每一个instance共享
//注:若让所有模板instance共享一个变量,则将共享变量放在一个非模板基类中
class Base {static int count = 0};
template<typename T> Derive: public Base{...};
- leading typename 和 dot template:
//模板编译分两步,读模板和实例化,读时遇到一些符号编译器不知是Type/object/func,一般两种情况:
//leading template告诉编译器此符号为一个Type。
template<typename T>
typename T::size_type munge(const T& t){
typename T::size_type * i(T::npos);
}
//dot template(obj.template)告诉编译器该符号是一个隶属于obj的模板成员函数:
struct ExampleClass{ template <typename U> void m(); }; //成员函数为模板
ExampleClass ex;
ex.template m<int>(); //需要ex.template,不然编译不过
二、MetaProgramming的常用工具:模板类
1.enum
/static constexpr
:Value
和Type
的实现v(no variable in MetaProgramming)
// ::Value
template <size_t N> struct IsEven{
enum { value = ( N %2 ) ? 0 : 1 };
};
template <typename T> struct SizeOfT{
static constexpr unsigned value = sizeof(T);
};
SizeOf<T>::Value
// ::Type
template <typename T> struct TPtr{
typedef T* type;
}
2. Partial Specialization: RemoveConst
// remove_const
template<typename T> struct remove_const{ typedef T type; };
template<> struct remove_const<const T>{ typedef T type; };
// remove volatile
template<typename T> struct remove_volatile{ typedef T type; };
template<> struct remove_volatile<volatile T>{ typedef T type; };
// remove_cv
template<typename T> struct remove_cv{
typedef typename remove_const<typename remove_volatile<T>::type>::type type;
};
3. Specialization: Check Type
// is_char
template<typename T> struct is_char{ enum { value = 0 }; };
template<> struct is_char<char>{ enum { value = 0 }; };
// isAnyChar (char with cv)
template<typename T> struct isAnyChar{
enum { value = is_char<std::remove_cv<T>::type>::value };
};
4. Recursion (Done by Partial Specialization)
template<size_t N> struct Sum{
enum { value = N + Sum<N-1>::value };
};
template<> struct Sum<1> {
enum { value = 1 };
};
三、MetaProgramming的用武之地
1. 编译时数值计算:(constexpr函数也可以)
(1). Log(N)
template <size_t N> struct Log2 {
enum { value = 1 + Log2<N/2>::value };
}
template <> struct log2<1> {
enum { value = 0 };
}
// or use constexpr function
constexpr size_t log2(size_t n){
return (( n<2 ) ? 1 : 1 + log2( n/2 ));
}
(2). If-then-else (std::conditional)
template <bool B, typename T, typename U> struct conditional{
typedef T type;
}
template <typename T, typename U> struct conditional<false, T, U>{
typedef U type;
}
// or value if use size_t
(3). Compile assert before C++11
template <size_t N> struct isPowerOf2{
enum { value = (N & (N-1)) };
};
template <bool C> struct StaticAssert; // undefine
template <> struct StaticAssert<true> {};
2. 代码生成 (Code generation) 加速运算
(1). generate by template argument (Pow<N>()(x)
, faster than std::math)
一个广泛的错误写法
// 产生了很多模板函数 Pow<N>()(double), 但这是个错误写法
template <size_t N> struct Pow {
double operator()(double x) const{
return (N%2) ? (x*Pow<N/2>()(x) * Pow<N/2>()(x))
: (N ? Pow<N/2>()(x) * Pow<N/2>()(x))
: 1;
};
};
如果模板Recursive有条件分支和三目运算,则必须保证所有的表达式都有效,编译器会查找所有的情况。而不仅仅是true
的那个。
正确写法: 偏特化+默认模板参数->将分支判断移出函数内部
template <size_t N, bool N % 2> struct PowHelper{
double operator()(double x) const { return Pow<N/2>()(x) * Pow<N/2>()(x);};
};
template <size_t N> struct PowHelper<N, false>{
double operator()(double x) const { return x*Pow<N/2>()(x) * Pow<N/2>()(x);};
};
template <> struct PowHelper<1, true>{
double operator()(double x) const { return x; };
};
template <> struct PowHelper<0, false>{
double operator()(double x) const { return 1; };
};
// 封装起来以始终用default
template <size_t N> struct Pow{
double operator()(double x) const { return Pow<N>()(x); };
};
3. 自适应代码 Self-Adaptive Code
(1). BitString, 从BitString中根据制定下标得到bit数值
template <typename T> BitString{
T* data;
enum { SHIFT = Log2<sizeof(N)>::value + 3};
enum { MASK = T(1) << SHIFT -1 };
bool getBit(size_t idx) const {
T word = data[ idx >> SHITF ]; // get the word the bit index in
T mask = T(1) << (i & MSAK);
return word & mask;
}
}
(2). deque, 双边队列
data[BlockSize*BlockNum],类似于BitString,Word为BlockIdx。
4. 类型操纵: Type Manipulate
(1). 检测Type类型:
template<class T> struct IsPtr{
enum { value = 0 };
typedef false_type type;
};
template<> struct IsPtr<T*>{
enum { value = 1 };
typedef true_type type;
};
(2). 类型转换:
//也可以利用Undefine来定义失败情况
template <class T> struct remove_ptr{
typedef T type;
};
template <> struct remove_ptr<T*>{
typedef T type;
}
5. 类型依赖代码(Type-dependent code)
(1). Sort by pointer/non-pointer Type
template<class T, bool Ptr> struct SortByType{....}
template<class T> struct SortByType<T, true>{....}
// for all type,
// 1. is a (use derive)
template<class T> struct SortContainer : public SortByType<T, is_ptr<T>::value> {....}
// 2. have a (use using or typedef)
template<class T> struct SortContainer{
using Impl = SortByType<T, is_ptr<T>::value>;
typedef SortByType<T, is_ptr<T>::value> type;
};
(2). Select to use internal sort or std::sort
template<class T> struct SortContainer: public Impl<T, has_sort<T>::value>{...};
6. 用SFINA来选择性编译:
(1). 判断类中是否有对应的成员函数:基本思路是偏特化两个函数(一个能匹配到成员函数,另一个匹配不到),两个函数采用为不同类型的返回值。最后通过返回值类型看出匹配的是哪个。
// example 1: before C++11
template<class T> struct hasSort{
template<class U, size_t (U::*)()> struct SFINAE {};
template<class U> static char test(SFINAE<U, &U::sort>*);
template<class U> static int test(...);
static constexpr bool Has = sizeof(test<T>(0)) == sizeof(char);
}
// example 2
template<class T> struct hasSort{
template<class U> static auto test(void*) -> decltype(&U::sort, std::true_type());
template<class U> static auto test(...) -> std::false_type;
enum { value = sizeof(hasSort::test<T>(NULL)) == sizeof(std::true_type) };
};
(2). 判断是不是POD类型
template <class T> isClass{
typedef char yes; typedef int no;
template<class U> yes test(void* U::*); // is have member
template<class U> no test(...);
enum { value = sizeof(isClass::test<T>(NULL)) == sizeof(isClass::yes) };
};
(3). 检查一个expression是不是可以 compile
template<class U> yes test(char*(a)[sizeof(EXPR)]); //用sizeof检查EXPR是否存在