C++11實現placeholder

文章分析怎樣在C++11中實現簡單的placeholder。ios


首先看看什麼是placeholder:
for_each(arr.begin(), arr.end(), cerr << _0 << endl);
當中arr的類型是vector<int>,第三個參數是一個表達式,表達式中有一個操做數是_0。第三個參數含義是對這個表達式進行求值,獲得一個對象x,x擁有operator ()(int a)的成員函數。整個表達式意味着把arr的每一個元素b取出來。而後調用x(b)。而x(b)的效果是將b輸出到一行當中。當中的_0就是placeholder,在表達式中做爲一個佔位符存在,等待外部給出_0的類型和值的時候,整個表達式再求值。

這是個很是酷的語法糖。在C++11中可以用lambda表達式取代,只是弄明確怎麼實現對於模板元編程的能力會有所提高。如下來分析一下怎樣去實現placeholder:

考慮_0, _1這些佔位符,首先搞明確它們在C++中的語法成份。出現在表達式中,而且表達式不是處於某個模板的環境。可以確定在編譯時能知道表達式的所有操做數的類型。

_0可以是一個形如被定義爲make_placeholder0()的宏。也可以是T0 _0;之類的一個對象。只是這沒有關係,咱們僅僅關心_0這個表達式的類型。可以看出_0應該具備類類型。operator << 能做用於該類型的對象上。c++

佔位符和其餘操做數進行運算,會產生新的類型,新的類型還可以參加運算,最後使得cerr << _0 << endl這個表達式具備某個類型。咱們假定這個產生的新的類型是TExpr。TExpr在運算後仍然是TExpr類型。假設把語法樹的結點用類型標註一下,對於表達式_0 + _1 - _2應該是例如如下結果編程


                       - : TExpr
               + : TExpr      _2 : T2

         _0 : T0  _1 : T1架構


TExpr將擁有operator ()。能夠求值。但是眼下看來TExpr上的operator () 具備不肯定性:
根結點上,operator將使用兩個操做數。把減法操做應用在兩個操做數上。
在左邊的結點上,是將加法應用在兩個操做數上。



因此TExpr中的operator ()是多態的。函數

假設將語法樹改成:
                       - : TExpr<MinusTag>
               + : TExpr<AddTag>      _2 : T2
         _0 : T0  _1 : T1
事情就變得easy一些,TExpr是參數化的,依據不一樣的Tag參數,在operator ()時有不一樣的行爲。

可以預見咱們應該構造一堆這種類:
post

template<typename L, typename R, typename T>
class TExpr;


template<typename L, typename R>
class TExpr<L, R, AddTag>
{
	L left_operand;
	R right_operand;
};


template<typename L, typename R>
class TExpr<L, R, SubTag>
{
	L left_operand;
	R right_operand;
};
當中L,R兩個類型多是T0。T1等佔位符的類型,或者是TExpr<,,,>,或者是其餘類型。但是這樣作有個缺點。把參數個數寫死了。因而將上面的AddTag和TExpr組合到一塊兒,造成
template<typename T1, typename T2>
struct TExprAdd
{
};
template<typename T1, typename T2>
struct TExprSub
{
};
進一步,將_0和_1的類型定義爲:
template<int v>
struct TExprBasic
{
};
並提供操做:
template<typename T1, typename T2>
TExprAdd<T1, T2> 
operator + (T1 a, T2 b)
{
	typedef TExprAdd<T1, T2> TI;
	return TI(a, b);
}


template<typename T1, typename T2>
TExprSub<T1, T2> 
operator - (T1 a, T2 b)
{
	typedef TExprSub<T1, T2> TI;
	return TI(a, b);
}
因而語法樹變爲:


                        - : TExprSub<TExprAdd<TExprBasic<0>, TExprBasic<1>>, TExprBasic<2>>
                + : TExprAdd<TExprBasic<0>, TExprBasic<1>>          _2 : TExprBasic<2>
     _0 : TExprBasic<0>  _1 : TExprBasic<1>

至此,咱們已經給出了一個可以用的placeholder的架構了。

咱們要求TExprAdd,TExprSub擁有一些共性,知足某個concept,這個concept就是TExpr。這個concept是本身在編程中心中默默創建的。固然也可以把這個concept用顯式的方式寫出來:
<pre name="code" class="cpp">template<typename T>
class TExpr
{
	// require 1:T具備計算返回類型的元函數get_result_type
	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename T::template get_result_type<TUPLE> Impl;
		typedef typename Impl::result_type result_type;
	};
	
	// require 2:T的對象應該具備operator () 成員函數模板,用於求值
	template<typename... Arg>
	auto operator () (Arg... arg)->typename get_result_type<std::tuple<Arg...>>::result_type
	{
		return impl.template operator () (std::forward<std::tuple<TL...>>(t));
	}
	
	T impl;
};

template<typename T1, typename T2>
TExpr<TExprAdd<T1, T2> >
operator + (T1 a, T2 b)
{
	typedef TExprAdd<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T1, typename T2>
TExpr<TExprSub<T1, T2> >
operator - (T1 a, T2 b)
{
	typedef TExprSub<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}
 
 
 
 

這樣就至關於描寫敘述了一個TExpr接口,有若干個詳細實現:TExprBasic。TExprAdd,TExprSub等。[在這裏接口的描寫敘述體現在TExpr對模板參數類型的依賴。要求T包括什麼樣的類型,要求T的對象有什麼樣的操做。接口的實現體現在把TExpr<...>做爲TExpr的模板參數。]嗯。也就是表達式模板技術。ui

因而咱們的語法樹中的-和+結點的類型分別變爲:this

TExpr<TExprAdd<TExprBasic<0>, TExprBasic<1>>>和TExpr<TExprSub<TExprAdd<TExprBasic<0>, TExprBasic<1>>, TExprBasic<2>>>添加TExpr優勢是,語法樹中的結點被分爲三類:TExpr<...>。TExprBasic<...>。以及其餘。這三類結點要求能夠計算返回類型,求值。

首先明白計算的輸入和輸出,返回類型的輸入是變長模板參數,返回一個返回類型,求值的輸入是tuple,返回一個值。而三種類型的編譯時和執行時求值策略很是清楚:1. TExpr可以直接依賴於模板參數。計算返回值類型,求值。2. TExprBasic可以直接計算返回值類型(輸入變長模板參數,返回該模板參數某個位置上的類型),直接求值(輸入tuple,返回tuple中某一項)。 spa

3. 其餘類型的計算返回值類型,求值方法就是自己的類型和值。code

因此可以很easy地針對這三類結點寫出處理類。另外還有TExprAdd之類的類沒有處理。這類結點的返回值類型計算需要分別計算其左操做數和右操做數的類型,然而在C++11中咱們可以方便地用decltype(TA()+TB())來進行處結果的類型推導。而求值也是分別計算左右操做數的值,而後中間填上一個相應的操做符,做爲一個表達式返回。[另外。還可以把TExprBasic做爲TExpr的參數,這樣所有的結點類型僅僅剩下TExpr和其餘類型了,更加方便管理。這個包裝過程僅僅需要改改代碼就能夠。

只是不改也能體現出一種思想,TExpr是遞歸的。TExprBasic是原子的,其餘類型是原子的。]在此基礎上,還引入兩類print函數。沒有參數的能輸出帶佔位符的語法樹。有參數的能輸出帶不少其餘信息的語法樹。

#include <iostream>
#include <tuple>
#include <vector>
#include <algorithm>
using namespace std;

template<int v>
struct int_{enum{value=v};};

template<typename T>
struct value_of{enum{value=-1};};

template<int v>
struct value_of<int_<v>>{enum{value=v};};

template<typename V1, typename V2>
struct max_type
{
	template<typename VA, typename VB, bool>
	struct max_type_impl
	{
		typedef VA result_type;
	};
	template<typename VA, typename VB>
	struct max_type_impl<VA, VB, true>
	{
		typedef VB result_type;
	};
	enum{value1=value_of<V1>::value, value2=value_of<V2>::value};
	typedef typename max_type_impl<V1, V2, (value1<value2)>::result_type result_type;
};

template<typename T>
struct TExpr
{
	
	TExpr(T o) : v(o){}
	
	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename T::template get_result_type<TUPLE> Impl;
		typedef typename Impl::result_type result_type;
	};
	template<typename... Arg>
	auto operator () (Arg... arg)->typename get_result_type<std::tuple<Arg...>>::result_type
	{
		return this->template evalue(std::tuple<Arg...>(arg...));
	}
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)->typename get_result_type<std::tuple<TL...>>::result_type
	{
		return v.template evalue(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... Arg>
	void print(Arg... arg)
	{
		this->template print(tuple<Arg...>(arg...));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		v.template print(std::forward<std::tuple<TL...>>(t));
	}
	void print()
	{
		v.print();
	}
	T v;
};

template<int v>
struct TExprBasic
{
	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename std::tuple_element<v, TUPLE>::type result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)->typename get_result_type<std::tuple<TL...>>::result_type
	{
		return std::get<v>(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << std::get<v>(std::forward<std::tuple<TL...>>(t));
	}
	void print()
	{
		cout << v;
	}
};

template<typename T>
struct is_expr
{
	enum{value=false};
};
template<typename T>
struct is_expr<TExpr<T>>
{
	enum{value=true};
};
template<int v>
struct is_expr<TExprBasic<v>>
{
	enum{value=true};
};

template<typename T, typename TUPLE>
struct get_result_type
{
	typedef T result_type;
};
template<typename T, typename TUPLE>
struct get_result_type<TExpr<T>, TUPLE>
{
	typedef typename TExpr<T>::template get_result_type<TUPLE>::result_type result_type;
};
template<int v, typename TUPLE>
struct get_result_type<TExprBasic<v>, TUPLE>
{
	typedef typename TExprBasic<v>::template get_result_type<TUPLE>::result_type result_type;
};

template<typename T>
struct print_helper_t
{
	typedef int_<is_expr<T>::value> U;
	
	print_helper_t(T a) : x(a){}
	
	void print()
	{
		print_impl(U());
	}
	void print_impl(int_<1>)
	{
		x.print();
	}
	void print_impl(int_<0>)
	{
		cout << x;
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		print_impl(std::forward<std::tuple<TL...>>(t), U());
	}
	template<typename... TL>
	void print_impl(std::tuple<TL...>&& t, int_<1>)
	{
		x.print(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print_impl(std::tuple<TL...>&& t, int_<0>)
	{
		cout << x;
	}
	T x;
};
template<typename T>
print_helper_t<T> print_helper(T x)
{
	return print_helper_t<T>(x);
}

template<typename T, typename TUPLE>
struct value_helper_t
{
	typedef int_<is_expr<T>::value> U;
	typedef typename ::get_result_type<T, TUPLE>::result_type result_type;
	
	value_helper_t(T a) : x(a){}
	
	template<typename... TL>
	result_type value(std::tuple<TL...>&& t)
	{
		return value_impl(std::forward<std::tuple<TL...>>(t), U());
	}
	template<typename... TL>
	result_type value_impl(std::tuple<TL...>&& t, int_<1>)
	{
		return x.template evalue(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	result_type value_impl(std::tuple<TL...>&& t, int_<0>)
	{
		return x;
	}
	T x;
};
template<typename T, typename TUPLE>
value_helper_t<T, TUPLE> value_helper(T x)
{
	return value_helper_t<T, TUPLE>(x);
}

template<typename T1, typename T2>
struct TExprAdd
{

	TExprAdd(T1 a, T2 b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename ::get_result_type<T1, TUPLE>::result_type TA;
		typedef typename ::get_result_type<T2, TUPLE>::result_type TB;
		typedef decltype(TA()+TB()) result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)-> typename get_result_type<std::tuple<TL...>>::result_type
	{
		return value_helper<T1, std::tuple<TL...>>(x).template value(std::forward<std::tuple<TL...>>(t)) + 
			   value_helper<T2, std::tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << "(";
		print_helper(x).template print(std::forward<std::tuple<TL...>>(t));
		cout << " + ";
		print_helper(y).template print(std::forward<std::tuple<TL...>>(t));
		cout << ")";
	}
	void print()
	{
		cout << "(";
		print_helper(x).template print();
		cout << " + ";
		print_helper(y).template print();
		cout << ")";
	}
	T1 x;
	T2 y;
};

template<typename T1, typename T2>
struct TExprSub
{

	TExprSub(T1 a, T2 b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename ::get_result_type<T1, TUPLE>::result_type TA;
		typedef typename ::get_result_type<T2, TUPLE>::result_type TB;
		typedef decltype(TA()-TB()) result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)-> typename get_result_type<std::tuple<TL...>>::result_type
	{
		return value_helper<T1, std::tuple<TL...>>(x).template value(std::forward<std::tuple<TL...>>(t)) - 
			   value_helper<T2, std::tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << "(";
		print_helper(x).template print(std::forward<std::tuple<TL...>>(t));
		cout << " - ";
		print_helper(y).template print(std::forward<std::tuple<TL...>>(t));
		cout << ")";
	}
	void print()
	{
		cout << "(";
		print_helper(x).template print();
		cout << " - ";
		print_helper(y).template print();
		cout << ")";
	}
	T1 x;
	T2 y;
};

template<typename T1, typename T2>
struct TExprMul
{

	TExprMul(T1 a, T2 b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename ::get_result_type<T1, TUPLE>::result_type TA;
		typedef typename ::get_result_type<T2, TUPLE>::result_type TB;
		typedef decltype(TA()*TB()) result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)-> typename get_result_type<std::tuple<TL...>>::result_type
	{
		return value_helper<T1, std::tuple<TL...>>(x).template value(std::forward<std::tuple<TL...>>(t)) * 
			   value_helper<T2, std::tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << "(";
		print_helper(x).template print(std::forward<std::tuple<TL...>>(t));
		cout << "*";
		print_helper(y).template print(std::forward<std::tuple<TL...>>(t));
		cout << ")";
	}
	void print()
	{
		cout << "(";
		print_helper(x).template print();
		cout << "*";
		print_helper(y).template print();
		cout << ")";
	}
	T1 x;
	T2 y;
};

template<typename T1, typename T2>
struct TExprDiv
{

	TExprDiv(T1 a, T2 b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename ::get_result_type<T1, TUPLE>::result_type TA;
		typedef typename ::get_result_type<T2, TUPLE>::result_type TB;
		typedef decltype(TA()/TB()) result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)-> typename get_result_type<std::tuple<TL...>>::result_type
	{
		return value_helper<T1, std::tuple<TL...>>(x).template value(std::forward<std::tuple<TL...>>(t)) / 
			   value_helper<T2, std::tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << "(";
		print_helper(x).template print(std::forward<std::tuple<TL...>>(t));
		cout << "/";
		print_helper(y).template print(std::forward<std::tuple<TL...>>(t));
		cout << ")";
	}
	void print()
	{
		cout << "(";
		print_helper(x).template print();
		cout << "/";
		print_helper(y).template print();
		cout << ")";
	}
	T1 x;
	T2 y;
};

template<typename T1, typename T2>
struct TExprComma
{

	TExprComma(T1 a, T2 b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename ::get_result_type<T1, TUPLE>::result_type TA;
		typedef typename ::get_result_type<T2, TUPLE>::result_type TB;
		typedef TB result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)-> typename get_result_type<std::tuple<TL...>>::result_type
	{
		value_helper<T1, std::tuple<TL...>>(x).template value(std::forward<std::tuple<TL...>>(t));
		return value_helper<T2, std::tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << "(";
		print_helper(x).template print(std::forward<std::tuple<TL...>>(t));
		cout << " , ";
		print_helper(y).template print(std::forward<std::tuple<TL...>>(t));
		cout << ")";
	}
	void print()
	{
		cout << "(";
		print_helper(x).template print();
		cout << " , ";
		print_helper(y).template print();
		cout << ")";
	}
	T1 x;
	T2 y;
};

template<typename T1, typename T2>
struct TExprShiftLeft
{

	TExprShiftLeft(T1 a, T2 b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename ::get_result_type<T1, TUPLE>::result_type TA;
		typedef typename ::get_result_type<T2, TUPLE>::result_type TB;
		typedef TA result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)-> typename get_result_type<std::tuple<TL...>>::result_type
	{
		return value_helper<T1, tuple<TL...>>(x).template value(std::forward<std::tuple<TL...>>(t)) << 
			   value_helper<T2, tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		typedef typename get_result_type<tuple<TL...>>::result_type result_type;
		output("(", int_<std::is_integral<result_type>::value>());
		print_helper(x).template print(std::forward<std::tuple<TL...>>(t));
		cout << " << ";
		print_helper(y).template print(std::forward<std::tuple<TL...>>(t));
		output(")", int_<std::is_integral<result_type>::value>());
	}
	void print()
	{
		//output("(", int_<std::is_integral<result_type>::value>());
		print_helper(x).template print();
		cout << " << ";
		print_helper(y).template print();
		//output(")", int_<std::is_integral<result_type>::value>());
	}
	void output(const char* s, int_<1>)
	{
		cout << s;
	}
	void output(const char* s, int_<0>)
	{
	}
	T1 x;
	T2 y;
};

template<typename T>
struct TExprShiftLeft<ostream, T>
{
	TExprShiftLeft(ostream& a, T b) : x(a), y(b){}

	template<typename TUPLE>
	struct get_result_type
	{
		typedef ostream& result_type;
	};
	template<typename... TL>
	auto evalue(std::tuple<TL...>&& t)->typename get_result_type<std::tuple<TL...>>::result_type
	{
		return x << value_helper<T, tuple<TL...>>(y).template value(std::forward<std::tuple<TL...>>(t));
	}
	template<typename... TL>
	void print(std::tuple<TL...>&& t)
	{
		cout << "cout << ";
		y.print(std::forward<std::tuple<TL...>>(t));
	}
	void print()
	{
		cout << "xout << ";
		y.print();
	}
	ostream& x;
	T y;
};

template<typename T1, typename T2>
TExpr<TExprAdd<T1, T2> >
operator + (T1 a, T2 b)
{
	typedef TExprAdd<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T1, typename T2>
TExpr<TExprSub<T1, T2> >
operator - (T1 a, T2 b)
{
	typedef TExprSub<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T1, typename T2>
TExpr<TExprMul<T1, T2> >
operator * (T1 a, T2 b)
{
	typedef TExprMul<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T1, typename T2>
TExpr<TExprDiv<T1, T2> >
operator / (T1 a, T2 b)
{
	typedef TExprDiv<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T1, typename T2>
TExpr<TExprComma<T1, T2> >
operator , (T1 a, T2 b)
{
	typedef TExprComma<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T>
TExpr<TExprShiftLeft<ostream, TExpr<T> > >
operator << (ostream& out, TExpr<T> x)
{
	typedef TExprShiftLeft<ostream, TExpr<T>> TI;
	return TExpr<TI>(TI(out, x));
}

template<int T>
TExpr<TExprShiftLeft<ostream, TExprBasic<T> > >
operator << (ostream& out, TExprBasic<T> x)
{
	typedef TExprShiftLeft<ostream, TExprBasic<T>> TI;
	return TExpr<TI>(TI(out, x));
}

template<typename T1, typename T2>
TExpr<TExprShiftLeft<TExpr<T1>, T2>>
operator << (TExpr<T1> e, T2 x)
{
	typedef TExprShiftLeft<TExpr<T1>, T2> TI;
	return TExpr<TI>(TI(e, x));
}

template<int v, typename T1>
TExpr<TExprShiftLeft<TExprBasic<v>, T1>>
operator << (TExprBasic<v> e, T1 x)
{
	typedef TExprShiftLeft<TExprBasic<v>, T1> TI;
	return TExpr<TI>(TI(e, x));
}

#define MAKE(x) TExprBasic<x> _ ## x
MAKE(0);MAKE(1);MAKE(2);MAKE(3);MAKE(4);MAKE(5);MAKE(6);MAKE(7);MAKE(8);MAKE(9);

int main()
{
	auto add = _0 + _1;
	cout << add(string("123"), "456") << endl;
	cout << add((int*)0, 1) << endl;
	cout << add("12345678", 1) << endl;
	
	auto test = (cerr << ((_0 << _1) + _2 * 0 - _1 / _2) << "___" << _3 << "___" << (int*)&main, cerr << _0, 1);
	test.print();
	cout << endl;
	test.print(4, 5, 6, string("c++"));
	cout << endl << "=";
	test(4, 5, 6, "hello world");
	cout << endl;
	
	int init = 0;
	vector<int> vec(8);
	for_each(vec.begin(), vec.end(), [&](int& a){a=++init;});
	for_each(vec.begin(), vec.end(), cout << _0);
	return 0;
}
相關文章
相關標籤/搜索