代码之家  ›  专栏  ›  技术社区  ›  Ayman

C++流操作符<和操纵器/格式化程序

  •  2
  • Ayman  · 技术社区  · 14 年前

    首先,我最近的工作大部分是Java。因此,即使我“知道”C++,我也不想用C++编写Java。

    C++模板是我回到Java时会非常怀念的东西。

    既然如此,如果我想创建一个新的流格式化程序,比如PIC,它将在它的构造函数中有一个STD::字符串参数。

    我希望用户能够编写如下内容:

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

    输出应该是

    Date is 2010/03/17

    我该怎么写电影课?当编译器看到cout时,编译器执行的底层步骤是什么?

    编辑 是不是更多的C++将代码转换为:

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

    可能更容易将pic函数作为独立函数(可能是模板)编写?

    4 回复  |  直到 14 年前
        1
  •  2
  •   anon    14 年前

    听起来您正在尝试编写另一种形式的printf()。我不确定这是个好主意,但是如果你决定这样做,你一定要把它作为一个自由函数来写,因为操纵器的问题(与使用格式字符串格式化无关)已经消失了。我还将避免从模板开始,并简单地设计和编写字符串版本:

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

    在编写这样一个函数之前,您必须在您的头脑中非常清楚它的语义是什么,我还不相信您是这样的。

        2
  •  2
  •   chub    14 年前

    你可以看看 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;
    
    }
    
        3
  •  0
  •   Matthieu M.    14 年前

    这里有两个问题。

    其中一个处理流操纵器,遵循引用。

    另一个处理格式问题。

    格式化是很困难的,尤其是您指示它的方式,因为它涉及到能够解析格式并生成一个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年2月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;
    }
    

    这里,你放弃解析。当然,这意味着格式是硬编码的…

        4
  •  0
  •   Ayman    14 年前

    这是我所做的初步解决方案。唯一的问题是我仍然不能将它模板化。如果我这样做了,那么给图片格式化程序打电话应该是 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;
    }