问题描述
我想从 variadic template
参数中检索每个参数的索引,目前我正在使用这种方法:
来自第三方库:
struct statement {
template <typename T>
void bind(int,T t) { // ....
}
};
我的代码:
template <unsigned Index,typename T>
void bind(statement& stmt,T t) {
stmt.bind(Index,t);
}
template <unsigned Index,typename T,typename... Args>
void bind(statement& stmt,T t,Args... args) {
bind<Index>(stmt,t);
bind<Index + 1>(stmt,args...);
}
template <typename... Args>
void bind_all(statement& stmt,Args... args) {
constexpr int Index = 0;
bind<Index>(stmt,args...);
}
用法:
statement stmt;
prepare(stmt,"insert into tab (a,b,c,d,e,f) values(?,?,?)");
bind_all(stmt,1,1.24f,3.14,"Hello",std::string{"World"},true);
我的问题:有没有更好的方法来实现这一点,以获得 variadic template
参数的序数?
编辑:
我想使用此实现来包装 sql prepared statement
并将特定参数绑定到特定索引。
这是我想要包装的代码示例,我不想单独列出每个绑定,而是想调用 bind_all
prepare(stmt,b) values (?,?);");
const int eight_int = 8;
stmt.bind(0,&eight_int);
const string eight_str = "eight";
stmt.bind(1,eight_str.c_str());
execute(stmt);
解决方法
通过简单的扩展(可以在 C++17 中使用 fold expression)
struct statement
{
template<class T>
void bind(int index,T&& arg)
{
// magic
}
};
template<class... Args>
void BindAll(statement& stmt,Args&&... args)
{
using swallow = int[];
int idx = 0;
(void)swallow{0,(void (stmt.bind(idx++,std::forward<Args>(args))),0)...};
}
我对 API 使用了一些自由,但我认为它与您的代码非常接近。
用法:
statement stmt;
BindAll(stmt,1,1.2,1.3f,true,"abc");
Demo
,为什么不为此使用 std::tuple
。
#include <utility>
#include <tuple>
template<typename T,std::size_t... Index>
void doBind(Statement& st,T const& tuple,std::index_sequence<Index...> const&)
{
// Using C++17 fold expression
((st.bind(Index,std::get<Index>(tuple))),...);
// Using C++11 dummy variable
int dummy[] = {0,(st.bind(Index,std::get<Index>(tuple)),0)...};
(void)dummy; // to prevent unused variable warning.
}
template<typename... Args>
void prepare(std::string const& sql,Args&&... args)
{
Statement statement;
prepare(statement,sql);
doBind(statement,std::make_tuple(args...),std::make_index_sequence<sizeof...(args)>());
execute(statement);
}
,
您可以使用 std::index_sequence
创建包含匹配索引的第二个模板参数包:
template <typename... Args,size_t... Is>
void bind_all_helper(std::index_sequence<Is...>,Args... args) {
int dummy[]{(bind<Is>(args),0)...};
(void)dummy; // just to avoid unused variable warnings
}
template <typename... Args>
void bind_all(Args... args) {
bind_all_helper(std::make_index_sequence<sizeof...(args)>{},args...);
}
这使用虚拟数组定义和逗号运算符来创建对 bind
的调用序列。例如,给定 bind_all(1,3.14)
,数组定义将扩展为如下所示:
int dummy[] {
(bind<0>(1),0),(bind<1>(3.14),0)
};
数组的每个元素最终都是 0
,但它的计算具有调用 bind<N>(arg)
的副作用。
在 C++17 中,dummy
数组定义可以用折叠表达式替换,但如果您仅限于 C++14,这显然不是一个选项。