5

さまざまなタイプの入力変数の可変数を取るC++テンプレート関数を書くことは可能ですか?(入力数は10と制限できます)例えば SQLクエリ文字列を実行し、供給タイプのSTDベクトルで、結果の行を節約機能sql_exec()を取る、すなわち可変の引数を持つC++テンプレート関数

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ベクトルに限る)されていたであろう

私はデフォルトの引数に関する機能を言わなかったし、私たちは

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

が、実際の取得として愚かだ。もちろん、

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

それはgccと-std=c++0xでコンパイルされます。しかし、明らかにsql_query()は可変長入力を受けず、2つのベクトルで呼び出す必要があります。また、私は現在のコンパイラのほとんどをポータブルなものにしたいと思っています。私が見落としたことが明らかなものは何ですか?私はデザインを変更することができ、おそらくboost::tupleなどを使用することができますが、私はそのような単純なインターフェイスが好きだったでしょう。

+1

このようにしますか? http://en.wikipedia.org/wiki/C%2B%2B0x#Variadic_templates – andrewdski

+0

はい、ありがとうございます。しかし、私はC++ 0xを避けようとしていますし、関数を定義する再帰的な方法もこの場合には困難になります。限られた最大入力数に満足している可能性があるので、別の方法がありますか? – tom

+0

バリデーシステンプレートをエレガントに使う方法はほぼ確実です。このようなテンプレートベースの思考には少し慣れていますが、おそらくバリデーショナルなテンプレートなしで作るものよりはるかに単純になるでしょう。 –

答えて

4

上記のように、Boost.PreprocessorはC++ 0xが利用できない場合に行く方法ですが、構文に慣れるまでに時間がかかります。以下の例は、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_LOCAL_ITERATE()が必要な理由を、ここでは0パラメータでレプリケーションを開始しますが、我々は1で開始する必要があるとして、我々はBOOST_PP_REPEAT(MAX_PARAMS, SQL_QUERY, ~)を使用することはできません、それはですより柔軟性があります。

6

C++ 0xでは、これは可変的なテンプレートによって実現されています(引数の数が膨大になり、実装固有の制限があります)。

C++ 03では、これはさまざまなアリティのテンプレート関数をたくさん生成するプリプロセッサマクロを持つことでエミュレートされます(Boost.Preprocessorを参照)。

私は、1から10の引数から "バインド"を生成するためにC++ 03技術を使用しました。

関連する問題