1. 程式人生 > >C++11可變數量模板引數可變型別模板引數並使用lamada函式呼叫使用範例

C++11可變數量模板引數可變型別模板引數並使用lamada函式呼叫使用範例

為了完成這個功能,耗費一整天。

背景是需要到一張表中查詢,條件不一樣,但是都可以通過PreparedStatement_setXX設定,想體驗一把C++11的高階模板特性,設計如下封裝

inline void set_para(PreparedStatement_T prepStat, int idx, std::string& str) {
			PreparedStatement_setString(prepStat, idx, str.c_str());
		}
		
		inline void set_para(PreparedStatement_T prepStat, int idx, int integer) {
			PreparedStatement_setInt(prepStat, idx, integer);
		}
這兩個函式就是實際設定的函式,關鍵是怎麼在主函式中分發給一個引數給這兩個函式中的一個去執行

於是設計如下主函式:

template<typename Bean, typename... Arguments>
		inline std::list<Bean> exec_sql(
				std::function<void (ResultSet_T, std::list<Bean>)> result_cb, 
				std::string &sql, 
				Arguments... params) {
			//int paras_size = sizeof...(Arguments);
			std::tuple<Arguments...> args = std::make_tuple(std::forward<Arguments>(params)...);	

			std::list<Bean> result;
			Connection_T conn = ConnectionPool_getConnection(connectionPool);
			TRY {
				PreparedStatement_T prepStat = Connection_prepareStatement(conn, sql.c_str());
				set_para(prepStat, args, gen_seq<sizeof...(Arguments)>{});
				result_cb(PreparedStatement_executeQuery(prepStat), result);
			} CATCH(SQLException) {
				throw std::runtime_error(Exception_frame.message);
			} FINALLY {
				Connection_close(conn);
			}
			END_TRY;
			return result;
		}		

用tuple這是關鍵點之一,建立一個分發函式是關鍵點之二,gen_seq是關鍵點之三
set_para(prepStat, args, gen_seq<sizeof...(Arguments)>{});

gen_seq是我在網上搜索“如何獲取可變模板引數索引”得到的答案,程式碼如下:

template <std::size_t... Ts>
	struct index {};

	template <std::size_t N, std::size_t... Ts>
	struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};

	template <std::size_t... Ts>
	struct gen_seq<0, Ts...> : index<Ts...> {};

這個程式碼比較深奧,我相信不是一般人能原創出來的,大家都是模仿。它的含義就是可以為每個可變引數生成一個索引,從0開始

gen_seq<sizeof...(Arguments)>
的展開過程介紹如下:

gen_seq(5)->gen_seq(4,4)->gen_seq(3,3,4)->gen_seq(2,2,3,4)->gen_seq(1,1,2,3,4)->gen_seq(0,0,1,2,3,4)->index(0,1,2,3,4)

於是乎就有了

index<Is...>即
index<0,1,2,3,4,5>
老外真是牛啊

分發函式如下:

template <typename... Args, std::size_t... Is>
		inline void set_para(PreparedStatement_T prepStat, std::tuple<Args...>& tup, index<Is...>) {
			auto l = {((set_para(prepStat, (Is + 1), std::get<Is>(tup))), 0)... };
			(void)l;
		}

這個函式也比較深奧啊,他的第一層意思是建立一個匿名物件,大括號裡的程式碼才會執行,第二層意思是有一個可變長度的表示式要展開,第三層意思每個表示式的含義是setpara,為什麼有可變表示式要展開呢,因為有可變索引的幫忙啊。

外面呼叫就很精練了,不比java的可讀性差

std::string sql = "select * from xxx where Id='?'...
return exec_sql<xxx>([=](ResultSet_T resultSet, std::list<xxx>){...}, sql, para0, para1.。。。); 
lamada表示式再簡單不過了,省略


不區分型別,不區分數量,一下全搞定。