折叠可变参数模板函数以在提供顺序的同时调用每个模板类型的函数
Fold on variadic template function to call a function on every template type while providing the order
我最近发现了 SQLite
,它对我来说似乎是一个很棒的嵌入式数据库。我能在它上面找到的唯一(而且非常小)的问题是关于它的 C 接口以及它如何迫使您键入大量样板代码,所以我想知道使用可变参数模板和 C++17 折叠功能是否可以改进那一点。
很遗憾,我知道我想要什么,但我一直无法自己开发。
解决方案的理想先决条件:
- 基于 C++17 或更低版本的解决方案(但遗憾的是不是 C++20)
- 摆脱辅助模板专用功能应该是一个很好的加分(也许是
if constexpr(type_trait)
的巧妙使用?)
这是我当前的代码,以及可变参数模板函数应如何运行的注释示例:
#include <iostream>
#include <tuple>
// Fake mock pretending the interface for SQLite3 (just to avoid have to install, link and include the real project).
// Please, do NOT change anything here.
unsigned char buffer[] = { 't', 'e', 's', 't' };
const int sqlite3_column_int(int iCol) { return 5; }
const unsigned char* sqlite3_column_text(int iCol) { return buffer; }
// End of unmodificable mock.
// Auxiliary functions:
template<typename T>
T sqlite3_column(int col);
template<>
int sqlite3_column<int>(int col) { return sqlite3_column_int(col); }
template<>
std::string sqlite3_column<std::string>(int col) { return reinterpret_cast<const char*>(sqlite3_column_text(col)); }
// What I would like to have available:
template<typename... Args>
auto sqlite3_record() -> std::tuple<Args...>
{
return std::make_tuple((sqlite3_column<Args>(N?),...));
}
//It should behabe as this example:
//std::tuple<int, int, std::string, std::string> sqlite3_record_example()
//{
// return std::make_tuple( sqlite3_column<int>(0), sqlite3_column<int>(1), sqlite3_column<std::string>(2), sqlite3_column<std::string>(3) );
//}
int main()
{
auto [i1, i2, s1, s2] = sqlite3_record<int, int, std::string, std::string>();
std::cout << i1 << " / " << i2 << " / " << s1 << " / " << s2;
//It should return 5 / 5 / test / test
}
这里是 Coliru link: http://coliru.stacked-crooked.com/a/52e5acd9d92a39e8
请原谅可怕的N?
模板参数位置。
template<typename... Args, std::size_t...Is>
auto sqlite3_record(sqlite3_stmt* statement, std::index_sequence<Is...>)
{
return std::make_tuple(sqlite3_column<Args>(statement, Is)...);
}
template<typename... Args>
auto sqlite3_record(sqlite3_stmt* statement){
return sqlite3_record<Args...>(statement, std::make_index_sequence<sizeof...(Args)>{});
}
你只需要数数,对吧?
为了计算包装数量,我们制作了第二包,其中包含数量。
然后我们可以一次展开两个包,只要它们大小相同即可。
std::make_index_sequence<N>{}
returns 类型为 index_sequence<0, 1,
... , n-2, n-1>
的对象,因此通过在函数签名中匹配我们可以获得包含我们想要的索引的包.
最简单的方法是制作一个辅助函数来获取该包,并在那里进行双参数包扩展,但还有其他方法可以获取整数包。
我最近发现了 SQLite
,它对我来说似乎是一个很棒的嵌入式数据库。我能在它上面找到的唯一(而且非常小)的问题是关于它的 C 接口以及它如何迫使您键入大量样板代码,所以我想知道使用可变参数模板和 C++17 折叠功能是否可以改进那一点。
很遗憾,我知道我想要什么,但我一直无法自己开发。
解决方案的理想先决条件:
- 基于 C++17 或更低版本的解决方案(但遗憾的是不是 C++20)
- 摆脱辅助模板专用功能应该是一个很好的加分(也许是
if constexpr(type_trait)
的巧妙使用?)
这是我当前的代码,以及可变参数模板函数应如何运行的注释示例:
#include <iostream>
#include <tuple>
// Fake mock pretending the interface for SQLite3 (just to avoid have to install, link and include the real project).
// Please, do NOT change anything here.
unsigned char buffer[] = { 't', 'e', 's', 't' };
const int sqlite3_column_int(int iCol) { return 5; }
const unsigned char* sqlite3_column_text(int iCol) { return buffer; }
// End of unmodificable mock.
// Auxiliary functions:
template<typename T>
T sqlite3_column(int col);
template<>
int sqlite3_column<int>(int col) { return sqlite3_column_int(col); }
template<>
std::string sqlite3_column<std::string>(int col) { return reinterpret_cast<const char*>(sqlite3_column_text(col)); }
// What I would like to have available:
template<typename... Args>
auto sqlite3_record() -> std::tuple<Args...>
{
return std::make_tuple((sqlite3_column<Args>(N?),...));
}
//It should behabe as this example:
//std::tuple<int, int, std::string, std::string> sqlite3_record_example()
//{
// return std::make_tuple( sqlite3_column<int>(0), sqlite3_column<int>(1), sqlite3_column<std::string>(2), sqlite3_column<std::string>(3) );
//}
int main()
{
auto [i1, i2, s1, s2] = sqlite3_record<int, int, std::string, std::string>();
std::cout << i1 << " / " << i2 << " / " << s1 << " / " << s2;
//It should return 5 / 5 / test / test
}
这里是 Coliru link: http://coliru.stacked-crooked.com/a/52e5acd9d92a39e8
请原谅可怕的N?
模板参数位置。
template<typename... Args, std::size_t...Is>
auto sqlite3_record(sqlite3_stmt* statement, std::index_sequence<Is...>)
{
return std::make_tuple(sqlite3_column<Args>(statement, Is)...);
}
template<typename... Args>
auto sqlite3_record(sqlite3_stmt* statement){
return sqlite3_record<Args...>(statement, std::make_index_sequence<sizeof...(Args)>{});
}
你只需要数数,对吧?
为了计算包装数量,我们制作了第二包,其中包含数量。
然后我们可以一次展开两个包,只要它们大小相同即可。
std::make_index_sequence<N>{}
returns 类型为 index_sequence<0, 1,
... , n-2, n-1>
的对象,因此通过在函数签名中匹配我们可以获得包含我们想要的索引的包.
最简单的方法是制作一个辅助函数来获取该包,并在那里进行双参数包扩展,但还有其他方法可以获取整数包。