2010-03-17 116 views
2

首先,我最近的大部分工作都是Java。所以即使我「知道」C++,我也不想用C++編寫Java。C++流操作符<<和操縱器/格式化器

而C++模板是我回到Java時真的會想念的一件事。

既然這樣,如果我想創建一個新的流格式化程序,比如pic,那麼在它的構造函數中將有一個std :: string參數。

我希望用戶能夠寫類似:

cout << pic("Date is 20../../..") << "100317" << endl; 

輸出應該

Date is 2010/03/17

我怎樣寫的PIC類?當編譯器看到cout編譯器的底層步驟是什麼?

編輯 難道是更多的C++更改代碼爲:

cout << pic("Date is 20../../..", "100317") << endl; 

而且可能更容易編寫的PIC功能作爲一個獨立的功能(可能是模板)?

+1

Duplicate:http://stackoverflow.com/questions/535444/custom-manipulator-for-c-iostream – 2010-03-17 12:45:26

+0

謝謝!這就是我需要的。 – Ayman 2010-03-17 12:55:07

+0

刪除我的答案,因爲上述評論實際上證明你可以創建代理 – 2010-03-17 13:01:52

回答

2

這聽起來像你正在試圖寫一個替代形式的printf()。我不確定這是否是一個好主意,但是如果你決定這樣做,你應該把它作爲一個自由函數來編寫,因爲操縱器問題(與使用格式字符串格式化無關)會消失。我也將避免模板入手,並簡單地設計和編寫字符串版本:

void pic(ostream & os, const string & fmt, const string & val); 

寫這樣的功能之前,你必須在你的頭腦非常清楚它的語義,這我不相信你還沒有。

+0

我喜歡自由格式功能,並且確實編輯了我的帖子。這個電話的語義對我來說依然不清楚。但我認爲它應該像我建議的那樣可行? – Ayman 2010-03-17 13:32:57

2

你可能會看看boost::format庫。

出頭像這應該工作(如果你能買得起第一劈裂你的字符串)

#include <iostream> 
#include <string> 
#include <boost/format.hpp> 

int main() 
{ 
    const char* a = "102030"; 
    std::string year(a, a + 2); 
    std::string month(a + 2, a +4); 
    std::string day(a + 4); 

    std::cout << boost::format("Date is 20%1%/%2%/%3%")% year % month % day << std::endl; 

} 
+0

我用編譯器(非標準和遺留系統)提升了一些問題。 – Ayman 2010-03-17 13:30:35

0

這裏有2個問題。

一個涉及流操縱器,請按照報價。

另一個涉及格式問題。

格式化非常困難,特別是您指出它的方式,因爲它涉及到能夠解析格式並生成AST表示,然後調用它來實際格式化字符串。解析意味着你需要定義一個小文法等......

有像Boost.Spirit這樣的庫來處理解析/生成,它們比'simple'Boost.Format複雜得多(它本身就是不那麼簡單)。

現在,你可以放棄解析?

class Date 
{ 
public: 
    Date(year_t year, month_t month, day_t day); 

    year_t getYear() const; 
    month_t getMonth() const; 
    day_t getDay() const; 
private: 
    year_t mYear; 
    month_t mMonth; 
    day_t mDay; 
}; 

這個類的好處是多方面的:

  • 結構化信息:解析一個,如你所願
  • 驗證讀取儘可能多的:根的無效日期(?2010年02月29)
  • 不含糊:「100102」實際上是「2010年2月1日」還是「2010年1月2日」? (至少不是一次解析)

然後,你可以通過創建一個小格式引擎來做同樣的格式。

template <class T> 
class Formatter 
{ 
public: 
    virtual ~Formatter() {} 
    virtual Formatter* clone() const = 0; 
    virtual std::string evaluate(const T& item) const = 0; 
}; 

template <class T> 
class FormatterConstant: public Formatter 
{ 
public: 
    explicit FormatterConstant(const std::string& s): mValue(s) {} 
    virtual Formatter<T>* clone() const { return new FormatterConstant(*this); } 
    virtual std::string evaluate(const T&) const { return mValue; } 

private: 
    std::string mValue; 
}; 

template <class T> 
class FormatterComposite: public Formatter<T> 
{ 
    typedef std::vector< const Formatter<T>* > formatters_type; 
    typedef typename formatters_type::const_iterator const_iterator; 
public: 
    // Need suitable Copy and Assignment Constructors 
    ~FormatterComposite() 
    { 
    for(const_iterator it = mFormatters.begin(), end = mFormatters.end(); 
     it != end; ++it) delete *it; 
    } 

    virtual Formatter<T>* clone() const { return new FormatterComposite(*this); } 

    virtual std::string evaluate(const T& item) const 
    { 
    std::string result; 
    for(const_iterator it = mFormatters.begin(), end = mFormatters.end(); 
     it != end; ++it) result += (*it)->evaluate(); 
    return result; 
    } 

    void imbue(std::ostream& s) { mStream = &s; } 

    FormatterComposite& operator<<(const std::string& s) 
    { 
    mFormatters.push_back(new FormatterConstant<T>(s); } 
    return *this; 
    } 

    FormatterComposite& operator<<(const Formatter<T>& formatter) 
    { 
    mFormatters.push_back(formatter.clone()); 
    return *this; 
    } 

    std::ostream& operator<<(const T& item) const 
    { 
    return (*mStream) << this->evaluate(item); 
    } 

private: 
    std::ostream* mStream; 
    formatters_type mFormatters; 
}; 

template <class T> 
FormatterComposite& operator<<(std::ostream& s, FormatterComposite& c) 
{ 
    c.imbue(s); 
    return c; 
} 


// Usage 
class DateOfYear: public Formatter<Date> 
{ 
public: 
    Formatter<Date>* clone() const { return new DateOfYear(*this); } 
    std::string evaluate(const Date& d) const { return toString(d.getYear()); } 
}; 

extern const DateOfYear year; 

int main(int argc, char* argv[]) 
{ 
    FormatterComposite<Date> formatter; 
    Date date; 
    std::cout << formatter << "Date is 20" 
      << year << "/" << month << "/" << day << date; 
    return 0; 
} 

在這裏,你放棄解析。當然這意味着格式是硬編碼的...

+0

我真的很感謝你爲此付出的努力和努力。然而,它比需求更復雜,與要求不符。我可能應該這麼說。我給出的是一個例子,你的帖子與這個特定的例子非常相關。 實際需要的是一個簡單的通用格式化器,適用於任何類型或字符串。我用約10行代碼寫了格式化程序。它完全符合我的需求。我想要的和在這裏要求的是一種簡單的方法,它對於'API'用戶來說很直觀。 – Ayman 2010-03-18 04:45:42

+0

哦,不用擔心,我只回答我覺得有趣的問題!我想看看你選擇了哪個解決方案,你可以發佈代碼嗎? – 2010-03-18 07:24:27

0

這是我所做的最初的解決方案。唯一的問題是,我仍然無法對它進行模板化。如果我這樣做,然後調用圖片格式化器看起來像pic<float>("$(...)", 2.56)和代碼會很混亂。

#include <iostream> 
#include <string> 

using namespace std; 

class pic { 
private: 
    const string& _v; 
    const string& _pic; 
public: 

    pic(const string& p, const string& v) : _v(v), _pic(p) { 
    } 

    friend ostream & operator<<(ostream& os, const pic& p) { 
     bool done = false; 
     int pi = 0; 
     int vi = 0; 
     while (!done) { 
      os << (p._pic[pi] == '.' ? p._v[vi++] : p._pic[pi]); 
      done = ++pi > p._pic.length() || vi > p._v.length(); 
     } 
     return os; 
    } 
}; 

int main(int argc, char** argv) { 
    cout << "The formatted date is: " << pic("20../../..", "100317") << endl; 
    return 0; 
}