2011-11-02 47 views
3

我希望能夠做到:C++字符串流來的ostream串

foo(stringstream()<<"number = " << 500); 

編輯:單行的解決方案是至關重要的,因爲這是記錄的目的。這些將圍繞代碼進行。

foo內部會打印字符串到屏幕或類似的東西。

,因爲現在的stringstream的經營者< <回報的ostream &,Foo的簽名必須是:

foo(ostream& o); 

,但我怎麼能轉換的ostream &爲字符串? (或char *)。 實現此用例的不同方法也受到歡迎。

+0

'foo'確實需要'ostream'嗎?或者,如果它需要'stringstream&'或者可能是一個'std :: string const&quot;它會被接受嗎? –

+0

讓它在一行中工作我相信它的確如此 – Leo

+0

難道你不能爲此創建一個宏,因爲它是C++嗎?那麼你會有'foo(MACRO_NAME(「number =」<< 500))''。 – hochl

回答

5

顯而易見的解決方案是在中使用。但是給定的 代碼仍然不起作用。 (你的例子將編譯,但它不會做你認爲應該做的 )。表達式std::ostringstream()是一個臨時的 ,你不能初始化一個臨時的非const引用, 和std::operator<<(std::ostream&, char const*)的第一個參數 是一個非const引用。 (您可以撥打 暫時撥打會員功能,如std::ostream::operator<<(void const*)。所以代碼 將編譯,但它不會做你的期望。

可以解決此問題,使用類似:

foo(std::ostringstream().flush() << "number = " << 500); 

std::ostream::flush()返回非const引用,所以沒有 進一步的問題。而在新創建的流上,這是一個無操作。 不過,我認爲你會同意它不是最優雅或直觀的 解決方案。

我通常在這種情況下,要做的就是創建一個包裝類,其中 包含它自己的std::ostringstream,並提供了一個模板 成員operator<<其轉發到包含 std::ostringstream。您的功能foo將採取const 對此—的引用,或者我所做的是直接將析構函數調用 foo,以便客戶端代碼甚至不必擔心它 它;它是這樣的:

log() << "number = " << 500; 

log()返回包裝類的實例函數(見下文 ),這個類的(最終)析構函數調用你的函數 foo

這有一個小問題。返回值可能會被複制, 並在複製後立即被破壞。這將破壞我剛剛解釋的 ;實際上,由於std::ostringstream不是 可複製的,它甚至不會編譯。這裏的解決方案是將所有的 實際邏輯,包括std::ostringstream和 析構函數邏輯的實例調用foo在一個單獨的實現類中,具有 公共包裝器有一個boost::shared_ptr它,並轉發。或者 只是重新實現了一下共享指針邏輯的類:

class LogWrapper 
{ 
    std::ostringstream* collector; 
    int* useCount; 
public: 
    LogWrapper() 
     : collector(new std::ostringstream) 
     , useCount(new int(1)) 
    { 
    } 

    ~LogWrapper() 
    { 
     -- *useCount; 
     if (*useCount == 0) { 
      foo(collector->str()); 
      delete collector; 
      delete useCount; 
     } 
    } 

    template<typename T> 
    LogWrapper& operator<<(T const& value) 
    { 
     (*collector) << value; 
     return *this; 
    } 
}; 

注意,很容易擴展,以支持可選的記錄;只需要 爲LogWrapper提供了一個構造函數,它將collector設置爲 NULL,並在operator<<中對此進行測試。

編輯:其他

一件事發生在我:你可能會想檢查 析構函數是否被稱爲異常的結果,在這種情況下,不叫 foo。從邏輯上講,我倒是希望你 可能得到的唯一的例外是std::bad_alloc,但總是會有誰 寫類似用戶:

log() << a + b; 

其中+是拋出一個用戶定義的過載。

+0

有趣的使用引用計數來允許不同的'LogWrapper'對象重用相同的'ostringstream'。在編輯過程中,您將如何檢測我們是否正在展開堆棧? –

+0

+1。輝煌的解釋。 :-) – Nawaz

+0

@DavidRodríguez-dribeas'std :: uncaught_exception'。 (我承認,在實踐中,我很少打擾,但是因爲我發佈了應該是一個很好的例子,所以我不妨試着儘可能正確。) –

0

既然你轉換爲字符串反正,爲什麼不

void foo(const std::string& s) 
{ 
    std::cout << "foo: " << s << std::endl; 
} 

... 

std::stringstream ss; 
ss << "number = " << 500; 
foo(ss.str()); 
+1

,因爲這是一個三班輪 – Leo

+0

然後請提到你想要使它成爲一個單線。 – hochl

+0

編輯,對於延遲 – Leo

0

這是不可能的。正如名稱ostream所暗示的那樣,它用於輸出,用於寫入。您可以將參數更改爲stringstream&。該課程的方法爲str(),它返回std::string供您使用。

編輯我沒有看到operator <<返回ostream&的問題。所以我想你不能簡單地在函數參數列表中編寫你的語句,但必須先寫出它。

+0

對不起,但我不得不在另一行中定義stringstream每次我想使用foo(....) – Leo

+0

這是真的。也許你可以在'foo'中引用這個引用,但我不確定這是否可以用'std :: ostream'的後代來實現。 – Constantinius

3

你可以使用代理對象來做這件事;這是一個有點框架的,但如果你想在很多地方使用這個符號那麼它可能是值得的:

#include <iostream> 
#include <sstream> 

static void foo(std::string const &s) 
{ 
    std::cout << s << std::endl; 
} 

struct StreamProxy 
{ 
    std::stringstream stream; 
    operator std::string() { return stream.str(); } 
}; 

template <typename T> 
StreamProxy &operator<<(StreamProxy &s, T v) 
{ 
    s.stream << v; 
    return s; 
} 

static StreamProxy make_stream() 
{ 
    return StreamProxy(); 
} 

int main() 
{ 
    foo(make_stream() << "number = " << 500); 
} 

這個程序打印

number = 500 

的想法是有一個小包裝類,可以隱式轉換爲std::string<<運營商只需轉發到包含的std::stringstream。該make_stream()功能,嚴格來說是沒有必要的(你也可以說StreamProxy(),但我認爲它看起來有點漂亮。

+0

基本思想是健全的,但是按照書面,它不會編譯,因爲你不能用'StreamProxy'臨時調用'operator <<'。 –

+0

您可以創建一個單例對象,並在'operator std :: string'中清除它的內容。但我不確定這是否是好設計;) – hochl

+0

@James Kanze:我只是在等待另一個版本的時候,很快就用MSVC6(是的,是的,我知道...)嘗試了它。確實有可能還有其他問題。我希望我能得到大致的想法。 :-) –

0

可以圍繞創建一個小包裝std::ostringstream將轉換回std::string上使用,並有功能拍攝一個std::string const &。第一種方法該解決方案可以在這個answer可以發現不同的問題。

最重要的是,你可以根據需要增加對操縱的支持(std::hex)。

+0

這基本上是我提出的解決方案,但是有幾點需要注意:當然,operator <<必須是一個成員(或者你可以說一些關於const的性質,使得操作符<<'對包裝器採取const引用)。 –

1

一對夫婦的不僅僅是通過Frerich拉貝提出的很好的代理解決方案的其他選項:

  • 在定義記錄功能的標題定義靜態字符串流變量,並在您的調用用逗號日誌記錄功能,以便通過此變量而不是由流插入運算符返回的ostream&。您可以使用日誌記錄宏來隱藏這個醜陋。這個解決方案的問題在於它有點難看,但這是一種常用的日誌記錄方法。

  • 請勿使用C++ I/O。改爲使用可變參數C型解決方案。傳遞格式字符串作爲第一個參數,其餘參數是該格式字符串的目標。這個解決方案的一個問題是,即使你的編譯器足夠聰明以確保printf及其堂兄弟安全,編譯器可能不會知道這個新函數是printf系列的一部分。儘管如此,這也是一種常用的方法。

3

我會建議你使用這個工具的結構:

struct stringbuilder 
{ 
    std::stringstream ss; 
    template<typename T> 
    stringbuilder & operator << (const T &data) 
    { 
     ss << data; 
     return *this; 
    } 
    operator std::string() { return ss.str(); } 
}; 

而且使用它作爲:

void f(const std::string & s); 

int main() 
{ 
    char const *const pc = "hello"; 

    f(stringbuilder() << '{' << pc << '}'); 

    //this is my most favorite line 
    std::string s = stringbuilder() << 25 << " is greater than " << 5 ; 
} 

演示(有幾個例子):http://ideone.com/J995r

更多在我的博客上:Create string on the fly just in one line

0

如果你不介意使用的宏功能,可以使日誌記錄功能接受const string&,並使用下面的宏

#define build_string(expr) \ 
    (static_cast<ostringstream*>(&(ostringstream().flush() << expr))->str()) 

而且假設你已經foo簽名void foo(const string&),你只需要一行代碼

foo(build_string("number = " << 500)) 

這受到詹姆斯坎澤關於static_caststringstream.flush的回答的啓發。沒有.flush()上述方法失敗,意外輸出。

請注意,此方法不應該泄漏內存,因爲無論是否在指針形式中,臨時值仍然分配在堆棧上,因此在返回時會被銷燬。