C++具有多个类型列表的可变参数函数模板

发布于 2025-01-10 13:27:23 字数 1829 浏览 3 评论 0原文

编译器资源管理器演示显示了我发现的有效内容以及显示我想要的注释部分,但这行不通。

我是 C++ 新手,我正在尝试用 C++20 编写一个 sqlite3 接口,该接口对查询参数和返回的列类型进行类型检查。我已经被困了好几天了,读了这么多。我确信其中之一有我的答案,但我只是不太了解这些东西,无法弄清楚我的问题是什么:

最终,这就是我想要的 任何意见

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);
}

将不胜感激。

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.

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. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

べ映画 2025-01-17 13:27:23

continue

Are you sure that you need template specialization?

What about simply using overloading?

If you switch the Input... / Output... in the template declaration

template <typename ... Outputs, typename ... Inputs>

you can explicit the Output... list in the call and the Input... list is deduced from the inputs... arguments

std::vector<std::tuple<Outputs...>> Query(const std::string &sql, Inputs... inputs) {}

You can call Query() as follows

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().

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);
}
夏至、离别 2025-01-17 13:27:23

continue

Writing std::format is extremely hard. Use the same approach as std::cout - one object that you can << add to.

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. 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';
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文