在可变参数模板扩展中增加int的安全方法是什么?

Oz.*_*Oz. 6 c++ variadic-templates c++11

我正在尝试围绕用C编写的SQL库实现C++ 11包装.C库具有单独的函数,用于从需要列索引的SQL语句中获取不同的数据类型.一个简单的方法是下面的原型,但有一个关键的缺陷:它依赖于参数执行的顺序,这是不安全的(也可能有编译器错误,没有测试它).

问题:在可变参数模板扩展中安全增加变量的独立于平台的方法是什么?

template< typename... ColumnTypes >
void SQLStatement::execute( std::function< void( ColumnTypes... ) > rowCallback ){
    while( this->nextRow() ){
        int column = 0;
        rowCallback( this->getColumn< ColumnTypes >( column++ )... );
        //                            unreliable increment ^
    }
}

template< typename T >
T SQLStatement::getColumn( const int columnIdx ){}

template<>
inline int SQLStatement::getColumn< int >( const int columnIdx ){
    return sql_library_column_int( this->nativeHandle, columnIdx );
}

// Other getColumn specializations here...
Run Code Online (Sandbox Code Playgroud)

mfo*_*ini 5

这似乎有效.你只需要适应一些事情:

#include <functional>
#include <iostream>
#include <cstddef>

void foo(int a, float b, int c) {
    std::cout << a << ", " << b << ", " << c << std::endl;
}

template<typename T>
T getColumn(int index) {
    return T(index);
}

template<size_t... indexes>
struct index_tuple {};

template<size_t head, size_t... indexes>
struct index_tuple<head, indexes...> {
    typedef typename index_tuple<head-1, head-1, indexes...>::type type;
};

template<size_t... indexes>
struct index_tuple<0, indexes...> {
    typedef index_tuple<indexes...> type;
};

template<typename... Args>
struct make_index_tuple {
    typedef typename index_tuple<sizeof...(Args)>::type type;
};

template<typename... ColumnTypes, size_t... indexes>
void execute(const std::function<void(ColumnTypes...)> &callback, index_tuple<indexes...>) {
    // this should be done for every row in your query result
    callback(getColumn<ColumnTypes>(indexes)...);
}

template<typename... ColumnTypes>
void execute(const std::function<void(ColumnTypes...)> &callback) {
    execute(
        callback, 
        typename make_index_tuple<ColumnTypes...>::type()
    );
}

int main() {
    std::function<void(int, float, int)> fun(foo);
    execute(fun);
}
Run Code Online (Sandbox Code Playgroud)

在这里演示.需要注意的是功能foo仅用于显示索引正确递增,就像return T(index);getColumn.


jog*_*pan 4

虽然 mfontanini 的解决方案有效并且很好,因为它在编译时执行递增列索引的计算,但我认为值得指出的是,对于如何在 a 中递增 int 的问题也有一个直接的答案可变参数包扩展。(不幸的是,由于错误,它似乎不适用于 GCC,请参阅最后的警告。)

\n\n

答案基于这样一个事实:虽然函数调用中参数的求值是无序的,但列表初始化中参数的求值却不是:

\n\n
\n

(\xc2\xa78.5.4/4) 在花括号初始化列表的初始值设定项列表中,初始值设定项子句,包括由包扩展 (14.5.3) 产生的任何初始值设定项子句,按照它们出现的顺序进行计算。也就是说,与给定初始值设定项子句关联的每个值计算和副作用都在与初始值设定项列表的逗号分隔列表中跟随它的任何初始值设定项子句相关联的每个值计算和副作用之前排序。
\n [ 注意:无论初始化的语义如何,此评估顺序都成立;例如,当初始化列表的元素被解释为构造函数调用的参数时,即使通常调用的参数没有顺序约束,它也适用。\xe2\x80\x94 尾注]

\n
\n\n

因此,如果您将函数调用转换为基于大括号初始化列表的内容,您将获得所需的效果:

\n\n
rowCallback(std::tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... });\n
Run Code Online (Sandbox Code Playgroud)\n\n

std::tuple使用列表初始化来初始化(注意大括号{ ... }),因此column++副作用将按从左到右的顺序执行。

\n\n

如果按上面的方式编写,这意味着您需要进行更改rowCallback()以使其接受 astd::tuple而不是参数列表。如果您不喜欢这样,您可以创建一个单独的模板函数,它对扩展元组产生的参数call_on_tuple(fun,tup)调用任何函数。我曾经在这里描述过如何执行此操作,或者如果您愿意,您可以使用我的 GitHub 存储库中的内容。funtuprlxutil::call_on_tuple

\n\n

你的execute函数看起来像这样:

\n\n
template <typename... ColumnTypes>\nvoid execute(function<void(ColumnTypes...)> rowCallback)\n{\n  using std::tuple;\n  using rlxutil::call_on_tuple;\n\n  int column = 0;\n  call_on_tuple(rowCallback,\n                tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... });\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

警告:这在 GCC 中无法按预期工作。我相信这是因为这里报告的错误:http://gcc.gnu.org/bugzilla/show_bug.cgi?id =51253 。

\n