2011-10-07 17 views
9

私はVisual Studio 2008を使用していますが、可変引数リストなしの文字列フォーマット機能を実装します。pre-C++ 0x(VS2008)で "Variadic Template"を実装する方法は?

pre-C++ 0x(VS2008)で「Variadic Template」を実装する方法は?

ブーストのようなライブラリがありますか?

これを実装する別の方法ですか?

ここは私のサンプルコードです。 (私はVS2008を使用していますので、もちろん、これは遵守することはできません。)

bool VarPrint(std::ostringstream& out, const std::string& s) 
{ 
    std::string::size_type offset = 0; 
    if((offset = s.find("%")) != std::string::npos) 
    { 
     if(!(offset != s.size() - 1 && s[offset + 1] == '%')) 
     { 
      ASSERT(!"Missing Arguments!"); 
      return false; 
     } 
    } 
    out << s; 
    return true; 
} 

template<typename T, typename... Args> 
bool VarPrint(std::ostringstream& out, const std::string& s, const T& value, const Args&... args) 
{ 
    std::string::size_type prev_offset = 0; 
    std::string::size_type curr_offset = 0; 
    while((curr_offset = s.find("%", prev_offset)) != std::string::npos) 
    { 
     out << s.substr(prev_offset, curr_offset); 
      if(!(curr_offset != s.size() - 1 && s[curr_offset + 1] == '%')) 
     { 
      out << value; 
      if(curr_offset + 2 < s.length()) 
       return VarPrint(out, s.substr(curr_offset + 2), args...);     return true; 
     } 

     prev_offset = curr_offset + 2; 
     if(prev_offset >= s.length) 
      break; 
    } 
    ASSERT(!"Extra Argument Provided!"); 
    return false; 
} 
+0

任意の量の引数が必要な場合は不可能です。 1〜10個の引数のような、束縛された数の引数の実装を提供するだけです。 – Dani

+0

あなたの例をコンパイルする: 'prev_offset> = s.length' - >'() 'が' length'関数を呼び出すことに注意してください:) –

+0

ありがとう。私は直接入力しながら '()'を見逃しました。 hah:D – winnerrrr

答えて

18

、あなたは異なる可能性があります。

  1. は、(例えばBoost.Preprocessorを使用して)0-N引数のオーバーロードを生成
  2. 利用短所を一覧表示します(cons(1)("some string")(foo)
  3. 使用をオブジェクトと最初のオプションは少しトリッキーであるいくつかの演算子(例えばoperator()、またはBoost.Format等operator%

をオーバーロード、私が感じる、なぜなら誰もが簡単にマクロを理解できるわけではありませんので、すぐにC++ 0xに移行する予定がある場合は、短期的な解決策として予約します。

3番目のオプションは、(多くの言語で%という記号で書式設定された)素敵なカスタムタッチを提供することができますが、この特定の「可変」機能が毎回どのように機能するかを覚えておく必要があります。

  • 定義はテンプレートのみを必要とするので、それは、より読みやすく、1よりmaintanableである:それは両方の問題を解決しているため

    私の個人的な好みはconsアプローチです。

  • あなたは一度短所-機械を定義し、あなたはそれが(と彼らは機能残る)任意の「可変引数」機能のために再使用することができますので、それはあなたが

を働くより一貫性があり、そして節約例えば、ここではそれが仕事ができる方法です:

ザ・この例で使用することが含まれます。

#include <cassert> 
#include <iostream> 
#include <string> 

をヘルパー価値を付加した結果タイプのために(それが先頭追加で、より効率的かもしれないが、それは、直感的である逆の順序で引数)を渡す意味します:魔法operator()

template <typename T, typename Next> struct Cons; 
struct ConsEmpty; 

template <typename Cons, typename U> 
struct cons_result; 

template <typename U> 
struct cons_result<ConsEmpty, U> { 
    typedef Cons<U, ConsEmpty> type; 
}; 

template <typename T, typename U> 
struct cons_result<Cons<T, ConsEmpty>, U> { 
    typedef Cons<T, Cons<U, ConsEmpty> > type; 
}; 

template <typename T, typename Next, typename U> 
struct cons_result<Cons<T, Next>, U> { 
    typedef Cons<T, typename cons_result<Next, U>::type> type; 
}; 

Consテンプレート自体は、値を追加します。それは異なったタイプの新しいアイテムを作成することに注意してください:

template <typename T, typename Next> 
struct Cons { 
    Cons(T t, Next n): value(t), next(n) {} 

    T value; 
    Next next; 

    template <typename U> 
    typename cons_result<Cons, U>::type operator()(U u) { 
    typedef typename cons_result<Cons, U>::type Result; 
    return Result(value, next(u)); 
    } 
}; 

struct ConsEmpty { 
    template <typename U> 
    Cons<U, ConsEmpty> operator()(U u) { 
    return Cons<U, ConsEmpty>(u, ConsEmpty()); 
    } 
}; 

template <typename T> 
Cons<T, ConsEmpty> cons(T t) { 
    return Cons<T, ConsEmpty>(t, ConsEmpty()); 
} 

AはそれでVarPrintを再訪:

bool VarPrint(std::ostream& out, const std::string& s, ConsEmpty) { 
    std::string::size_type offset = 0; 
    if((offset = s.find("%")) != std::string::npos) { 
     if(offset == s.size() - 1 || s[offset + 1] != '%') { 
      assert(0 && "Missing Arguments!"); 
      return false; 
     } 
    } 
    out << s; 
    return true; 
} 

template<typename T, typename Next> 
bool VarPrint(std::ostream& out, 
       std::string const& s, 
       Cons<T, Next> const& cons) 
{ 
    std::string::size_type prev_offset = 0, curr_offset = 0; 
    while((curr_offset = s.find("%", prev_offset)) != std::string::npos) { 
     out << s.substr(prev_offset, curr_offset); 
     if(curr_offset == s.size() - 1 || s[curr_offset + 1] != '%') { 
      out << cons.value; 
      if(curr_offset + 2 < s.length()) 
       return VarPrint(out, s.substr(curr_offset + 2), cons.next); 
      return true; 
     } 
     prev_offset = curr_offset + 2; 
     if(prev_offset >= s.length()) 
      break; 
    } 
    assert(0 && "Extra Argument Provided!"); 
    return false; 
} 

とデモ

int main() { 
    VarPrint(std::cout, "integer %i\n", cons(1)); 
    VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo")); 
} 

をあなたがideoneに出力を確認することができます。

integer 1 
mix of 2 and foo 
+0

涼しい〜あなたの詳細をありがとう! – winnerrrr

6

C++ 03には可変引数テンプレート機能はありません。 Boostやその他のよく設計されたライブラリは、さまざまな方法でこれを回避します。関数の場合、N + 1個のオーバーロードがあり、各オーバーロードは0からNまでの引数を取ることができます。クラスの場合、無効な型の既定のN個までの引数を持つ単一の定義を持つことができます。この上限は、通常はマクロによって設定可能です。これを高く設定するとコンパイル時にオーバーヘッドが発生し、それを低く設定するとユーザーに十分な引数を渡すことができなくなるためです。

具体的なケースでは、VarPrintを再帰的な方法で実装します。再帰の各ステップは、単一の引数を処理し、変更された書式文字列と再帰呼び出しを発行し、すべての左の値が1つの位置の左にシフトされます。 C++ 03では

+0

ありがとう。私はNオーバーロード機能を実装する以外に選択肢はないと思う。 :] – winnerrrr

+1

@winnerrrr:Boost.Formatは演算子のオーバーロードと遅延評価を使用するだけです。 '、'の代わりに '%'を引数の間に入れなければならず、それらは括弧で囲まれません。しかし、それは動作します。 –

関連する問題