tuple的本质是一个容纳各种数据类型成员的“结构体”。很早之前,我发现它和数据库的记录很像,都是字段的集合。用tuple可以做数据库编程的利器。
下面是一个基本框架,可抓取host应用程序的变量,可区分左值和右值。对于host应用程序的左值,希望它能同数据库api的绑定变量关联。很多数据库都支持绑定变量的思想。
对于右值,认为它在sql语句中起到直接量的作用,而右值往往来源于host应用程序的文字直接量
#include <iostream>
#include <sstream>
#include <type_traits>
namespace ext{
// tuple
template<class... _Types> class tuple;
template<> class tuple<> {};
template<class _This, class... _Rest>
class tuple<_This, _Rest...> : private tuple<_Rest...>
{
public:
_This _Myfirst;
typedef tuple<_Rest...> _Mybase;
template<class Head, class... Rest>
explicit tuple(Head&& arg0, Rest&&... args)
: _Mybase(std::forward<Rest>(args)...),
_Myfirst(std::forward<Head>(arg0))
{
}
};
// tuple_element
template<size_t _Index, class _Tuple> struct tuple_element;
// select first element
template<class _This, class... _Rest>
struct tuple_element<0, tuple<_This, _Rest...>>
{
typedef _This& type;
typedef tuple<_This, _Rest...> _Ttype;
typedef _This original_type;
};
// recursive tuple_element definition
template <size_t _Index, class _This, class... _Rest>
struct tuple_element<_Index, tuple<_This, _Rest...>>
: public tuple_element<_Index - 1, tuple<_Rest...> >
{
};
// get reference to _Index element of tuple
template<size_t _Index, class... _Types> inline
typename tuple_element<_Index, tuple<_Types...>>::type
get(tuple<_Types...>& _Tuple)
{
typedef typename tuple_element<_Index, tuple<_Types...>>::_Ttype _Ttype;
return (((_Ttype&)_Tuple)._Myfirst);
}
//if the element at _Index is rvalue,then return true
template<size_t _Index, class... _Types> inline
bool is_rvalue(tuple<_Types...>& _Tuple)
{
typedef typename tuple_element<_Index, tuple<_Types...>>::original_type original_type;
return std::is_rvalue_reference<original_type>::value;
}
template<class... Types>
tuple<Types&&...> forward_as_tuple(Types&&... args)
{
return ext::tuple<Types&&...>(std::forward<Types>(args)...);
}
//封装了values子句,所需要的一些变量或文字常量
template<class... Types>
class values_wrapper{
public:
values_wrapper(Types&&... args) : tp_(std::forward<Types>(args)...){};
ext::tuple<Types&&...> tp_;
};
//利用函数的参数推导能力,简化客户程序员编写代码
template<class... Types>
values_wrapper<Types&&...> values(Types&&... args){
return values_wrapper<Types&&...>(std::forward<Types>(args)...);
}
//judge if one type is tuple
template<class T>
struct is_tuple{
static const bool value = false;
};
template<class... Types>
struct is_tuple<ext::tuple<Types...> >{
static const bool value = true;
};
};
class C_SQL{
public:
template<typename... Args>
friend C_SQL& operator%(C_SQL& sql , ext::values_wrapper<Args...> &tp);
friend C_SQL& operator%(C_SQL& sql, const char* stat);
std::stringstream txt_;
std::string sep_; //逗号分隔符
void print(){
std::cout << txt_.str() << std::endl;
}
};
template<class Tuple, std::size_t N>
struct process_values {
static void do_process(Tuple& t, C_SQL& sql)
{
process_values<Tuple, N - 1>::do_process(t,sql);
sql.txt_ << sql.sep_;
sql.sep_ = ",";
if (ext::is_rvalue<N>(t)){
sql.txt_ << ext::get<N>(t);
}
else{
sql.txt_ << "?";
}
return;
}
};
template<class Tuple>
struct process_values<Tuple, 0>{
static void do_process(Tuple& t, C_SQL& sql)
{
sql.txt_ << sql.sep_;
sql.sep_ = ",";
if (ext::is_rvalue<0>(t)){
sql.txt_ << ext::get<0>(t);
}
else{
sql.txt_ << "?";
}
return;
}
};
template<typename... Args>
C_SQL& operator%(C_SQL& sql, ext::values_wrapper<Args...>& va){
sql.txt_ << "values ( ";
sql.sep_ = "";
process_values<decltype(va.tp_), sizeof...(Args)-1 >::do_process(va.tp_, sql);
sql.txt_ << ")";
return sql;
}
C_SQL& operator%(C_SQL& sql, const char* stat){
sql.txt_ << stat;
return sql;
}
int main()
{
using namespace ext;
C_SQL sql;
int a = 0;
sql % "insert into t_test(col1,col2,col3) " % values(a,2,3);
sql.print();
return 0;
}
c++11的出现,values支持任意长度的参数列表,而在c++11之前,boost的tuple支持的参数列表个数是受限的。
通过打印print语句,发现左值a被正确识别,也被替换为?。右值2,3被sql语句保留为文字量。测试基本通过。
基本的模板技术都是从网友博客拷贝出来修改的,技术细节和原理不是非常透彻的理解(但也不是一窍不通,否则也改不出来)。
改进方向:1)host变量要真正的绑定到数据库api中去
2)这个例子只支持了values子句,还需要支持like子句,between子句,in子句等。in 子句可以用数组实现,like子句可以用std::pair实现。
此框架可以无缝的整合host应用程序的本地变量,完全隐藏数据库api的实现细节。也可以彻底的同传统的sql拼接字符串的原始低效方法说再见。