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

我正在尝试围绕用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...

解决方法

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

答案是基于以下事实:虽然函数调用中的参数的计算结果未被排序,但列表初始化中参数的计算结果不是:

(§8.5.4/4) Within the initializer-list of a braced-init-list,the initializer-clauses,including any that result from pack expansions (14.5.3),are evaluated in the order in which they appear. That is,every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.
[ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example,it applies when the elements of the initializer-list are interpreted as arguments of a constructor call,even though ordinarily there are no sequencing constraints on the arguments of a call. — end note ]

因此,如果您将函数调用转换为基于brace-init-list的内容,您将获得所需的效果

rowCallback(std::tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... });

这使用list-initialization初始化std :: tuple(注意大括号{…}),因此列副作用将按从左到右的顺序执行.

如果按上面所述编写,这意味着您需要更改rowCallback()以使其接受std :: tuple而不是参数列表.如果你不喜欢这个,你可以创建一个单独的模板函数call_on_tuple(fun,tup),它调用扩展元组tup产生的参数上的任何函数.我曾经描述过如何执行此操作here,或者如果您愿意,可以使用my GitHub repository中的rlxutil :: call_on_tuple.

你的执行函数如下所示:

template <typename... ColumnTypes>
void execute(function<void(ColumnTypes...)> rowCallback)
{
  using std::tuple;
  using rlxutil::call_on_tuple;

  int column = 0;
  call_on_tuple(rowCallback,tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... });
}

警告:这与GCC没有预期的效果.我相信这是因为这里报告的错误http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...