【问题标题】:Fold on variadic template function to call a function on every template type while providing the order折叠可变参数模板函数以调用每个模板类型的函数,同时提供顺序
【发布时间】:2021-04-05 23:36:17
【问题描述】:

我最近发现了SQLite,它对我来说似乎是一个很棒的嵌入式数据库。我能找到的唯一(也是非常小的)问题是关于它的 C 接口以及它如何迫使您输入大量样板代码,所以我想知道使用可变参数模板和 C++17 折叠功能是否可以改进那一点。

不幸的是,我知道我想要什么,但我无法自己开发它。

解决方案的理想先决条件:

  1. 基于 C++17 或更低版本(但遗憾的是不是 C++20)的解决方案
  2. 摆脱辅助模板专门功能应该是一个高加分(也许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 链接:http://coliru.stacked-crooked.com/a/52e5acd9d92a39e8

请原谅可怕的N?模板参数的地方。

【问题讨论】:

    标签: c++ c++17 variadic-templates fold


    【解决方案1】:
    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&lt;N&gt;{} 返回一个 index_sequence&lt;0, 1, ... , n-2, n-1&gt; 类型的对象,因此通过匹配函数签名,我们可以得到一个包含我们想要的索引的包。

    最简单的方法是创建一个辅助函数来获取该包,并在那里进行双参数包扩展,但还有其他方法可以获取整数包。

    【讨论】:

    • 好的,我终于有时间了解您的答案并且看起来不错。否则我认为return std::make_tuple(sqlite3_column&lt;Args&gt;(statement, Is),...); 行最后一个额外的逗号会使编译失败。
    • @pablo 为额外的, 道歉,我在复制您的伪代码时错过了删除它。哎呀。
    • 非常感谢您的清晰解释。很高兴找到人们分享他们的知识!
    猜你喜欢
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多