[英]C++ variadic function template with multiple typelists
Compiler Explorer Demonstration shows what I have found that works as well as a commented out section showing what I want, but that doesn't work. Compiler Explorer Demonstration显示了我发现的有效内容,以及显示我想要但不起作用的注释部分。
I am new to C++ and I am trying to write a sqlite3 interface in C++20 that has type checking for query parameters and returned column types.我是 C++ 的新手,我正在尝试用 C++20 编写一个 sqlite3 接口,它对查询参数和返回的列类型进行类型检查。 I have been stuck for several days now and have read so much.我已经被困了好几天了,读了这么多。 I am sure that one of these has my answer, but I just don't understand this stuff well enough to figure out what my issue is:我确信其中一个有我的答案,但我只是不太了解这些东西,无法弄清楚我的问题是什么:
Ultimately, this is what I would like to work最终,这就是我想要的工作
template <class... T>
struct Typelist {};
struct Database {
Database(const string &sql) {}
template <class Input, class Output>
void Query(Input input) {}
// error: non-class, non-variable partial specialization
// 'Query<Typelist<Inputs ...>, Typelist<Outputs ...> >' is not allowed
template <class... Inputs, class... Outputs>
vector<tuple<Outputs...>> Query<Typelist<Inputs...>, Typelist<Outputs...>>(
const string &sql, Inputs... inputs) {}
};
int main() {
Database db(":memory:");
vector<tuple<string, string>> people =
db.Query<Typelist<int, float>, Typelist<string, string>>(
"SELECT fname, lname FROM users WHERE id = ? AND somefloat = ?;", 1, 42.0f);
}
Any observations would be greatly appreciated.任何意见将不胜感激。
Are you sure that you need template specialization?您确定需要模板专业化吗?
What about simply using overloading?简单地使用重载怎么样?
If you switch the Input...
/ Output...
in the template declaration如果在模板声明中切换Input...
/ Output...
template <typename ... Outputs, typename ... Inputs>
you can explicit the Output...
list in the call and the Input...
list is deduced from the inputs...
arguments您可以在调用中显式显示Output...
列表,然后从输入中推导出Input...
列表inputs...
arguments
std::vector<std::tuple<Outputs...>> Query(const std::string &sql, Inputs... inputs) {}
You can call Query()
as follows您可以按如下方式调用Query()
std::vector<std::tuple<std::string, std::string>> people =
db.Query<std::string, std::string>(
"SELECT fname, lname FROM users WHERE id = ? AND somefloat = ?;", 1, 42.0f);A
Observe also that your compiler explore example compile if you simply rename Query2()
as Query()
.另请注意,如果您只是将Query2()
重命名为Query()
,您的编译器会探索示例编译。
The following is a full compiling example下面是一个完整的编译示例
#include <tuple>
#include <string>
#include <vector>
struct Database
{
Database(std::string const & sql)
{}
template <typename Input, typename Output>
void Query(Input input)
{}
template <typename ... Outputs, typename ... Inputs>
std::vector<std::tuple<Outputs...>>
Query(std::string const & sql, Inputs... inputs)
{}
};
int main()
{
Database db(":memory:");
std::vector<std::tuple<std::string, std::string>> people =
db.Query<std::string, std::string>(
"SELECT fname, lname FROM users WHERE id = ? AND somefloat = ?;",
1, 42.0f);
}
Writing std::format
is extremely hard.编写std::format
非常困难。 Use the same approach as std::cout
- one object that you can <<
add to.使用与std::cout
相同的方法 - 一个 object 您可以<<
添加到。
May this short example written in a very short time show you how to use sqlite3_str_appendf
to construct the query string and how to convert output parameters, but most importantly encourage learning a lot more about C++ programming.愿这个用很短的时间编写的简短示例向您展示如何使用sqlite3_str_appendf
构造查询字符串以及如何转换 output 参数,但最重要的是鼓励学习更多关于 C++ 编程的知识。 This is merely a template written in a very short time to show the interface, untessted - a real interface would be expected to be much more well thought through.这只是在很短的时间内编写的用于显示界面的模板,未经测试 - 一个真正的界面应该是经过深思熟虑的。
#include <sqlite3.h>
#include <iostream>
#include <stdexcept>
#include <vector>
#include <string_view>
#include <tuple>
#include <functional>
#include <algorithm>
// C callabck for sqlite3_exec
extern "C"
int query_C_trampoline(void *cookie, int n, char **d, char **c) {
auto f = reinterpret_cast<std::function<int(int, char **, char **)>*>(cookie);
return (*f)(n, d, c);
}
// the output conversion functions
template<typename T>
void query_assign(char *data, char *column, T& to);
template<>
void query_assign(char *data, char *column, std::string& to) {
to = data;
}
template<std::size_t I = 0, typename ...T>
void query_assign_recursive(char **data, char **column, std::tuple<T...>& res) {
query_assign(*data, *column, std::get<I>(res));
if constexpr (I + 1 != sizeof...(T)) {
query_assign_recursive<I + 1>(data++, column++, res);
}
}
struct Query {
sqlite3_str *s{};
sqlite3 *db{};
Query(sqlite3 *db) : db(db) {
s = sqlite3_str_new(db);
if (s == NULL) throw std::runtime_error("something");
}
~Query() {
// TODO free(s) at least
}
void _herr() {
if (sqlite3_str_errcode(s)) {
throw std::runtime_error("something");
}
}
Query& operator<<(const char *t) {
sqlite3_str_appendf(s, "%s", t);
_herr();
return *this;
}
Query& operator<<(int t) {
sqlite3_str_appendf(s, "%d", t);
_herr();
return *this;
}
Query& operator<<(float t) {
sqlite3_str_appendf(s, "%f", t);
_herr();
return *this;
}
template<typename ...T>
std::vector<std::tuple<T...>> exec() {
std::vector<std::tuple<T...>> ret{};
std::function<int(int, char **, char**)> cb = [&](int count, char **data, char **column) {
std::tuple<T...> col;
if (count != sizeof...(T)) throw std::runtime_error("count");
// recursively assing tuple elements, as above
query_assign_recursive(data, column, col);
ret.emplace_back(col);
return 0;
};
char *errmsg{};
// trampoline only calls std::function
int e = sqlite3_exec(db, sqlite3_str_value(s), query_C_trampoline, &cb, &errmsg);
if (e) std::runtime_error((const char *)errmsg);
return ret;
}
};
int main() {
sqlite3 *ppdb;
sqlite3_open("/tmp/a", &ppdb);
auto res =
(Query(ppdb)
<< "SELECT fname, lname FROM users WHERE id = "
<< 1
<< " AND somefloat = "
<< 42.0f
<< ";"
).exec<std::string, std::string>();
for (auto&& i : res) {
std::cout << std::get<0>(i) << ' ' << std::get<1>(i) << '\n';
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.