Reputation: 611
I have recently discovered SQLite
and it seems like a wonderful embedded DB to me. The only (and really small) issue that I can find on it is about its C interface and how it forces you to type a lot of boiler plate code, so I wonder if using variadic templates and C++17 fold features we can improve that point.
Unfortunately, I know what I want but I have not been able to develop it by myself.
Desirable preconditions for the solution:
if constexpr(type_trait)
?)This is my current code, together with a commented example of how the variadic template function should behave:
#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
}
Here is a Coliru link: http://coliru.stacked-crooked.com/a/52e5acd9d92a39e8
Please, forgive the horrible N?
for the template parameter place.
Upvotes: 0
Views: 400
Reputation: 275500
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)>{});
}
you just need to count, right?
To get a count of the packs, we make a 2nd pack with the count in it.
Then we can expand both packs at once, so long as they are the same size.
std::make_index_sequence<N>{}
returns an object of type index_sequence<0, 1,
... , n-2, n-1>
, so by matching in a function signature we can get a pack with the indexes we want.
The easiest way is to make a helper function to get at that pack, and do the double parameter pack expansion there, but there are other ways we could get at the pack of integers.
Upvotes: 1