c++具有可变参数的模板函数

发布于 2024-11-17 12:15:02 字数 938 浏览 6 评论 0原文

是否可以编写一个 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 或其他东西,但我喜欢这样一个简单的界面。

Is it possible to write a c++ template function which takes a variable number of input variables of different types (number of input can be limited to say 10)?
For example take a function sql_exec() which executes an sql query string and saves the resulting rows in std vectors of the type supplied, i.e.

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

Now my naive approach would have been (limited to max 2 vectors)

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) {
    ...
}

Of course that's stupid as I didn't tell the function about default arguments and we get

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

but actually it compiles with gcc and -std=c++0x. However, obviously sql_query() still doesn't take variable length input and needs to be called with 2 vectors. Also, I'd like to have something portable working on most of the current compilers. Anything obvious I've overlooked? I know I can change the design and maybe use boost::tuple or something else but I'd have liked such a simple interface.

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

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

发布评论

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

评论(2

私野 2024-11-24 12:15:02

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

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

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

In C++0x this achieved through variadic templates (and the number of arguments can get huge, limit being implementation specific).

In C++03, this is emulated by having preprocessor macros generating lots of template functions of various arity (see Boost.Preprocessor).

I've used the C++03 technic to generate the "bind" from 1 to 10 arguments and it works pretty well.

难得心□动 2024-11-24 12:15:02

如上所述,如果 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需要使用(),这样比较灵活。

As said above, Boost.Preprocessor is the way to go if C++0x is not available, although it takes a while to get used to the syntax. The example below demonstrate the way Boost.Preprocessor can be used to define functions with variable (but limited) number of arguments.

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

The preprocessor expands this to:

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

Note, here we can't use BOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~) as it starts replication with 0 parameters but we need to start with 1, that's why BOOST_PP_LOCAL_ITERATE() is needed which is more flexible.

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