C ++ операторы потоков << и манипуляторы / форматеры - PullRequest
2 голосов
/ 17 марта 2010

Во-первых, большая часть моей недавней работы была Java. Поэтому, хотя я «знаю» C ++, я не хочу писать Java на C ++.

И шаблоны C ++ - это одна вещь, которую мне будет очень не хватать при возвращении на Java.

Теперь, если это не так, если я захочу создать новый модуль форматирования потока, скажем, pic, в его конструкторе будет один параметр std :: string.

Я бы хотел, чтобы пользователь мог написать что-то вроде:

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

Вывод должен быть

Date is 2010/03/17

Как мне написать класс ПОС? когда компилятор видит cout, какие основные шаги выполняет компилятор?

Редактировать Будет ли больше C ++, чтобы изменить этот код на:

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

А может быть проще написать функцию pic как отдельную функцию (возможно, шаблон)?

Ответы [ 4 ]

2 голосов
/ 17 марта 2010

вы можете взглянуть на библиотеку 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;

}
2 голосов
/ 17 марта 2010

Похоже, вы пытаетесь написать альтернативную форму printf (). Я не уверен, что это хорошая идея, но если вы решите это сделать, вам обязательно следует написать ее как бесплатную функцию, потому что проблемы манипулятора (которые не имеют ничего общего с форматированием с использованием строки форматирования) исчезают. Я также хотел бы избегать шаблонов для начала и просто проектировать и писать строковую версию:

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

Прежде чем написать такую ​​функцию, вы должны очень четко подумать, какова ее семантика, а я вам не верю.

0 голосов
/ 18 марта 2010

Вот первоначальное решение того, что я сделал. Единственная проблема заключается в том, что я до сих пор не могу ее шаблонизировать. Если я это сделаю, то вызов средства форматирования pic будет выглядеть как 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;
}
0 голосов
/ 17 марта 2010

Здесь есть 2 вопроса.

Один имеет дело с потоковыми манипуляторами, следуйте цитате.

Другой имеет дело с проблемами форматирования.

Форматирование сложно, особенно то, как вы его указываете, потому что оно предполагает возможность анализа формата и генерации AST-представления, которое затем будет вызываться для фактического форматирования строки. Разбор означает, что вам нужно определить небольшую грамматику и т. Д. *

Существуют библиотеки, такие как Boost.Spirit, которые занимаются синтаксическим анализом / генерацией, и они намного сложнее, чем «простой» 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;
};

Преимущество этого класса несколько:

  • Структурированная информация: анализирует один, читает столько, сколько вы хотите
  • Проверка: вычеркнуть недопустимую дату (29 февраля 2010?)
  • Нет двусмысленности: действительно ли «100102» - это «1 февраля 2010» или «2 января 2010»? (хотя бы не разобрано)

Затем вы можете сделать то же самое для формата, создав небольшой механизм форматирования.

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

Здесь вы отказываетесь от разбора. Конечно, это означает, что формат жестко закодирован ...

...