2012-11-23 39 views
0

這不是C++ 11,這是C++ 03提高自己的執行追加前綴和後綴的流

問題

我有一個叫decorator類應該是能夠將文本添加到用戶通過operator<<傳遞給它的消息的兩端。

例如:

decorator dec( 
    "-> ",   // prefix 
    "\n"   // suffix 
); 
dec << "the value is: " << 33; 

這應該輸出:-> the value is 33\n


我的實現

dec << "the value is" 

當該代碼被處理,proxy operator<<(decorater & dec , const T & msg)被調用,decorater添加前綴,trea t消息並返回一個臨時代理對象。這個臨時對象將負責處理鏈條的其餘部分。

temporary_proxy_object << 33 

現在處理此代碼,臨時對象通過代理operator<<(const proxy &, const T &)處理整數。它還記錄了它已被operator<<唆使的事實。最後它返回一個臨時對象,它是它自己的副本。

由於沒有其他消息要處理,所以最後一個臨時對象單獨死亡。由於它沒有被operator<<索引,所以它調用一個成員函數decorater來讓它知道它是最後一個對象,裝飾者愉快地追加後綴。


我痛心

我最討厭這個實現是它依賴於編譯器的優化:

proxy operator(const proxy & p, const T & msg) 
{ 
    // some work here 
    return proxy(p); 
} 

沒有什麼能阻止從創建兩個臨時代理對象的編譯器,第一個在函數中,第二個作爲返回的臨時。如果是這樣的話,那麼我的實現將被搞砸,因爲第一個臨時對象不會被請求。


源代碼

#include <string> 
#include <sstream> 
#include <iostream> 

class decorater; 
class proxy 
{ 
public: 
    proxy(const decorater & creator), 
      proxy(const proxy & other); 

    void solicitate() const 
    { 
     m_solicitated = true; 
    } 

    ~proxy(); 

    const decorater & getDecorater() const; 


private: 
    mutable bool m_solicitated; 
    const decorater & m_creator; 
}; 

class decorater 
{ 
public: 
    decorater(const std::string & prefix, const std::string & suffix) 
     :m_ostr() 
     ,m_prefix(prefix) 
     ,m_suffix(suffix) 
    { 
    } 

    template<typename T> 
    proxy treat (const T & msg) const 
    { 
     m_ostr << msg; 
     return proxy(*this); 
    } 

    void appendPrefix() const 
    { 
     m_ostr << m_prefix; 
    } 

    void appendSuffix() const 
    { 
     m_ostr << m_suffix; 
    } 

    void print() 
    { 
     std::cout << m_ostr.str(); 
    } 

private: 
    mutable std::ostringstream m_ostr; 
    std::string m_prefix, m_suffix; 
}; 

template <typename T> 
proxy operator<<(decorater & dec , const T & msg) 
{ 
    dec.appendPrefix(); 
    return dec.treat(msg); 
} 

proxy::proxy(const decorater & creator) 
    :m_solicitated(false) 
    ,m_creator(creator) 
{ 
} 

proxy::proxy(const proxy & other) 
    :m_solicitated(false) 
    ,m_creator(other.m_creator) 
{ 
} 

proxy::~proxy() 
{ 
    if (!m_solicitated) 
     getDecorater().appendSuffix(); 
} 

const decorater & proxy::getDecorater() const 
{ 
    return m_creator; 
} 

template <typename T> 
proxy operator<<(const proxy & p, const T & msg) 
{ 
    p.getDecorater().treat(msg); 
    p.solicitate(); 

    return p; 
} 

int main() 
{ 
    decorater dec("-> ", "\n"); 
    dec << "the value is: " << 33; 
    dec.print(); 
} 
+0

如果這很關注你,是否有理由使用代理對象而不是修改裝飾器狀態並通過引用返回它(iostream插入器的工作方式?)無論如何...如果你只是想創建一個代理對象,然後傳遞它,你可以使用'shared_ptr '(TR1或boost) - 複製將會很便宜,並且會在最後清理。 – HostileFork

+0

@HostileFork我不介意修改裝飾器狀態,實際上這是我在決定使用代理對象之前所做的。該解決方案的問題是,我無法知道鏈的最後一個元素何時被處理。換句話說,我還沒有找到一種方法來知道何時追加「後綴」部分。 – qdii

回答

0

我想我會使用類似的東西給ostream_iterator:

// surround_iterator.h 
#if !defined(SURROUND_ITERATOR_H_) 
#define SURROUND_ITERATOR_H_ 
#include <ostream> 
#include <iterator> 
#include <string> 

template <class T, class charT=char, class traits=std::char_traits<charT> > 
class surround_ostream_iterator : 
    public std::iterator<std::output_iterator_tag, void, void, void, void> 
{ 
    std::basic_ostream<charT,traits> *os; 
    std::basic_string<charT> prefix; 
    std::basic_string<charT> suffix; 

public: 

    typedef charT char_type; 
    typedef traits traits_type; 
    typedef std::basic_ostream<charT, traits> ostream_type; 

    surround_ostream_iterator(ostream_type &s) 
     : os(&s) 
    {} 

    surround_ostream_iterator(ostream_type &os, std::basic_string<charT> const &p, std::basic_string<charT> const &s) 
     : os(&os), 
      prefix(p), 
      suffix(s) 
    {} 

    surround_ostream_iterator<T, charT, traits> &operator=(T const &item) 
    { 
     *os << prefix << item << suffix; 
     return *this; 
    } 

    surround_ostream_iterator<T, charT, traits> &operator*() { 
     return *this; 
    } 

    surround_ostream_iterator<T, charT, traits> &operator++() { 
     return *this; 
    } 

    surround_ostream_iterator<T, charT, traits> &operator++(int) { 
     return *this; 
    } 
}; 

#endif 

和快速的測試程序,以顯示它是如何工作的:

#include "surround_iterator.h" 
#include <algorithm> 
#include <iostream> 

int main() { 

    int vec[] = { 1, 2, 3, 4, 5, 6, 7, 8}; 

    std::copy(vec, vec+8, 
     surround_ostream_iterator<int>(std::cout, "->", "\n")); 
    return 0; 
} 

結果:

->1 
->2 
->3 
->4 
->5 
->6 
->7 
->8 

無所[缺乏]優化器折斷,更習慣用法,並且如在上面的櫻桃,代碼位短,更簡單的爲好。

+0

我很抱歉,但我不明白這是如何解決我的問題:如果我正確地閱讀'surround_ostream'會添加一個前綴和一個後綴類型的單個項目T.但我的問題是添加這些前綴後綴爲由<<分隔的元素鏈。如果我錯過了明顯的事情,我很抱歉。 – qdii

+0

@qdii:不,只是沒有在你的問題中說清楚。爲了包圍一個對象鏈,我想我會使用一個類似於我在[先前的答案](http://stackoverflow.com/a/10110623/179910)中發佈的事務類。 –

0

返回包裝在shared_ptr中的代理對象將使您在機械意義上在那裏。副本將很便宜,並且析構函數將在不再需要臨時對象時運行。雖然異常會試圖將析構函數用作同步點來執行輸出工作,而不是僅僅清理。

我覺得它像是歸結爲風格差的問題,如果

a << "foo"; 
a << "bar"; 

...做的東西比

a << "foo" << "bar"; 

這在我看來是更好地使用類似endl不同更明確地標記運行的結束,或者像@JerryCoffin所建議的那樣在事務中進行換行。