5

Có thể viết một hàm mẫu C++ có một số biến đầu vào của các kiểu khác nhau (số đầu vào có thể bị giới hạn khi nói 10) không? Ví dụ có một chức năng sql_exec() mà thực hiện một chuỗi truy vấn sql và tiết kiệm các hàng kết quả là vectơ std của các loại cung cấp, ví dụ:C++ mẫu chức năng với các đối số biến

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 

Bây giờ đi đến ngây thơ của tôi sẽ là (giới hạn tối đa 2 vectơ)

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

Tất nhiên đó là ngu ngốc như tôi không nói với các chức năng về đối số mặc định và chúng tôi nhận

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

nhưng thực tế ly nó biên dịch với gcc và -std=c++0x. Tuy nhiên, rõ ràng là sql_query() vẫn không có độ dài biến đổi và cần được gọi với 2 vectơ. Ngoài ra, tôi muốn có một cái gì đó di động làm việc trên hầu hết các trình biên dịch hiện tại. Bất cứ điều gì rõ ràng tôi đã bỏ qua? Tôi biết tôi có thể thay đổi thiết kế và có thể sử dụng boost::tuple hoặc một cái gì đó khác nhưng tôi đã thích một giao diện đơn giản như vậy.

+1

Như thế này? http://en.wikipedia.org/wiki/C%2B%2B0x#Variadic_templates – andrewdski

+0

Vâng, cảm ơn. Tuy nhiên, tôi đang cố gắng tránh C++ 0x và cũng là cách đệ quy để xác định hàm sẽ làm cho mọi thứ trở nên khó khăn trong trường hợp này. Vì tôi hài lòng với số lượng đầu vào tối đa hạn chế có thể có một cách khác? – tom

+0

có gần như chắc chắn một cách để sử dụng các mẫu variadic thanh lịch. Loại tư duy dựa trên mẫu này có một chút quen thuộc với, nhưng nó có thể sẽ đơn giản hơn nhiều so với bất cứ thứ gì bạn muốn tạo mà không có các mẫu variadic. –

Trả lời

4

Như đã nói ở trên, Boost.Preprocessor là cách để đi nếu C++ 0x không khả dụng, mặc dù phải mất một lúc để làm quen với cú pháp. Ví dụ dưới đây minh họa cách Boost.Preprocessor có thể được sử dụng để định nghĩa các hàm có số đối số biến (nhưng giới hạn).

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

Các preprocessor mở rộng này để:

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

Lưu ý, ở đây chúng tôi không thể sử dụng BOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~) khi nó bắt đầu nhân lên với 0 thông số nhưng chúng ta cần phải bắt đầu với 1, đó là lý do tại sao BOOST_PP_LOCAL_ITERATE() là cần thiết mà linh hoạt hơn.

6

Trong C++ 0x điều này đạt được thông qua các mẫu variadic (và số đối số có thể nhận được rất lớn, giới hạn đang được triển khai cụ thể).

Trong C++ 03, điều này được mô phỏng bằng cách sử dụng các macro tiền xử lý tạo nhiều hàm mẫu của các tính chất khác nhau (xem Boost.Preprocessor).

Tôi đã sử dụng kỹ thuật C++ 03 để tạo "liên kết" từ 1 đến 10 đối số và nó hoạt động khá tốt.

Các vấn đề liên quan