为了完成这个功能,耗费一整天。
背景是需要到一张表中查询,条件不一样,但是都可以通过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表达式再简单不过了,省略
不区分类型,不区分数量,一下全搞定。