2011-10-07 28 views
9

我正在使用Visual Studio 2008,並且我想實現不帶變量參數列表的字符串格式化函數如何使用pre-C++ 0x(VS2008)實現「變量模板」?

如何使用pre-C++ 0x(VS2008)實現「變量模板」?

是否有任何圖書館實現這樣的提升?

或者另一種實現方法?

這是我的示例代碼。 (當然,這不能因爲我使用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

謝謝。我錯過了'()',而直接輸入。哈:D – winnerrrr

回答

18

在C++ 03,則具有不同的可能性:

  1. 生成0-N的參數(使用Boost.Preprocessor例如)
  2. 使用缺點-列表(cons(1)("some string")(foo)
  3. 使用重載對象和重載一些運營商(operator()例如,或operator%等Boost.Format庫)

第一個選項是有點麻煩,感覺,因爲不是每個人都可以很容易地理解宏,所以如果您計劃儘快遷移到C++ 0x,我只會保留它以用於短期解決方案。

第三個選項可能會提供一個很好的自定義觸摸(格式是用多種語言的%符號完成的),但這也意味着需要記住每次這個特定的「可變參數」函數是如何工作的。

我個人的偏好是cons方法,因爲它解決了兩個問題:

  • 的定義只涉及模板,所以它是更具可讀性和超過1 maintanable。
  • 您定義的利弊,機械一次,你可以重新使用它的任何「可變參數」功能(和他們保持功能),所以它是比較一致的,並保存你的工作

例如,這裏是如何可以工作:

的包括本例中將使用:

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

一種結果類型附加價值的助手(也可能是與預謀更有效,但這將意味着通過以相反的順序論點是反直覺):

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模板本身,附魔operator()附加價值。請注意,它與不同類型創建一個新的項目:

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

一個重新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。遞歸中的每一步都將處理一個參數,並使用修改後的格式字符串發出遞歸調用,並將所有左側值向左移動一個位置。

+0

謝謝。我想我別無選擇,只能實現N個重載函數。 :] – winnerrrr

+1

@winnerrrr:Boost.Format只是使用運算符重載和懶惰評估來做到這一點。你必須在參數之間放置'%'而不是''',而不是放在括號中。但它的工作。 –