【问题标题】:c++ template functions with variable arguments带有可变参数的 c++ 模板函数
【发布时间】:2011-06-29 15:00:13
【问题描述】:

是否可以编写一个 c++ 模板函数,它接受可变数量的不同类型的输入变量(输入的数量可以限制为 10 个)? 例如,采用一个函数sql_exec(),它执行一个 sql 查询字符串并将结果行保存在所提供类型的 std 向量中,即

std::vector<double> x,y;
std::vector<std::string> s;
std::string query="select * from ...";

sql_exec(query, s,x,y); // error if less than 3 rows or conversion not possible

现在我的幼稚方法是(限制为最多 2 个向量)

struct null_type {};
template <typename T1=null_type, typename T2=null_type>
void sql_query(const std::string& query_str, std::vector<T1>& col1,
           std::vector<T2>& col2) {
    ...
}

当然这很愚蠢,因为我没有告诉函数默认参数,我们得到了

error: default template arguments may not be used in function templates

但实际上它是用 gcc 和 -std=c++0x 编译的。但是,显然sql_query() 仍然不接受可变长度输入,需要用 2 个向量调用。另外,我希望在大多数当前编译器上都有一些可移植的东西。有什么明显的我忽略了吗?我知道我可以更改设计并可能使用boost::tuple 或其他东西,但我会喜欢这样一个简单的界面。

【问题讨论】:

  • 好的,谢谢。但是,我试图避免使用 C++0x,并且在这种情况下,定义函数的递归方式也会使事情变得困难。由于我对有限的最大输入数量感到满意,也许还有另一种方法?
  • 几乎肯定有一种方法可以优雅地使用可变参数模板。这种基于模板的思维需要一点时间来适应,但它可能会比你在没有可变参数模板的情况下制作的任何东西要简单得多。

标签: c++ templates template-meta-programming variadic-functions


【解决方案1】:

在 C++0x 中,这是通过可变参数模板实现的(参数的数量可能会变得很大,限制是特定于实现的)。

在 C++03 中,这是通过预处理宏生成大量不同数量的模板函数来模拟的(参见 Boost.Preprocessor)。

我使用 C++03 技术从 1 到 10 个参数生成“绑定”,效果很好。

【讨论】:

    【解决方案2】:

    如上所述,如果 C++0x 不可用,Boost.Preprocessor 是可行的方法,尽管需要一段时间才能习惯语法。下面的示例演示了如何使用 Boost.Preprocessor 定义具有可变(但有限)数量的参数的函数。

    #include <boost/preprocessor/repetition.hpp>
    #include <boost/preprocessor/iteration/local.hpp>
    #include <boost/preprocessor/iteration/iterate.hpp>
    
    #define MAX_PARAMS  2
    
    class sql {
    public:
       // definition of the function in macro form
       #define SQL_QUERY_DEF(z, n, unused)                                     \
       template <BOOST_PP_ENUM_PARAMS(n, class T)>                             \
       void query(const std::string& query,                                    \
                BOOST_PP_ENUM_BINARY_PARAMS(n, const T, & x) );
    
       // does the actual code replication of SQL_QUERY_DEF
       #define BOOST_PP_LOCAL_MACRO(n)  SQL_QUERY_DEF(~, n, ~)
       #define BOOST_PP_LOCAL_LIMITS    (1, MAX_PARAMS)
       #include BOOST_PP_LOCAL_ITERATE()
    
       ...
    };
    
    
    // two helper functions:
    // expands to var0.clear(); var1.clear(); ...
    #define SQL_VECTOR_CLEAR(z,i,var) var##i.clear();
    // expands to var0.push_back(this->get_col<T0>(0); ...
    #define SQL_VECTOR_PUSH_BACK(z,i,var) var##i.push_back(this->get_col<T##i>(i));
    
    // definition of the function in macro form
    #define SQL_QUERY(z, n, unused)                                               \
    template <BOOST_PP_ENUM_PARAMS(n, class T)>                                   \
    void sql::query(const std::string& query,                                     \
                      BOOST_PP_ENUM_BINARY_PARAMS(n, std::vector< T,>& x) ){      \
       this->do_query(query);                                                     \
       if(this->num_cols()<n){                                                    \
          throw std::runtime_error();                                             \
       }                                                                          \
       BOOST_PP_REPEAT(n, SQL_VECTOR_CLEAR, x)                                    \
       while(this->is_open()) {                                                   \
          BOOST_PP_REPEAT(n, SQL_VECTOR_PUSH_BACK, x)                             \
          this->step();                                                           \
       }                                                                          \
    }
    
    // does the actual code replication of SQL_QUERY
    #define BOOST_PP_LOCAL_MACRO(n)  SQL_QUERY(~, n, ~)
    #define BOOST_PP_LOCAL_LIMITS    (1,  MAX_PARAMS)
    #include BOOST_PP_LOCAL_ITERATE()
    

    预处理器将其扩展为:

    $ g++ -P -E sql.cpp | astyle
    
    class sql {
    public:
       template < class T0> void query(const std::string& query, const T0 & x0 );
       template < class T0 , class T1> void query(const std::string& query, const T0 & x0 , const T1 & x1 );
       ...
    };
    template < class T0> void sql::query(const std::string& query, std::vector< T0 >& x0 ) {
       this->do_query(query);
       if(this->num_cols()<1) {
          throw std::runtime_error();
       }
       x0.clear();
       while(this->is_open()) {
          x0.push_back(this->get_col<T0>(0));
          this->step();
       }
    }
    template < class T0 , class T1> void sql::query(const std::string& query, std::vector< T0 >& x0 , std::vector< T1 >& x1 ) {
       this->do_query(query);
       if(this->num_cols()<2) {
          throw std::runtime_error();
       }
       x0.clear();
       x1.clear();
       while(this->is_open()) {
          x0.push_back(this->get_col<T0>(0));
          x1.push_back(this->get_col<T1>(1));
          this->step();
       }
    }
    

    注意,这里我们不能使用BOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~),因为它以0参数开始复制,但我们需要从1开始,这就是为什么需要BOOST_PP_LOCAL_ITERATE(),它更灵活。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-08
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      • 2020-05-14
      相关资源
      最近更新 更多