Оператор потоков С++ ‹‹ и манипуляторы/форматеры

Во-первых, большая часть моей недавней работы была связана с Java. Поэтому, хотя я и «знаю» 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 как отдельную функцию (возможно, шаблон)?


person Ayman    schedule 17.03.2010    source источник
comment
Дубликат: stackoverflow.com/questions/535444/   -  person Georg Fritzsche    schedule 17.03.2010
comment
Удалил мой ответ, так как приведенный выше комментарий фактически доказывает, что вы можете создать прокси   -  person Grant Peters    schedule 17.03.2010


Ответы (4)


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

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

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

person Community    schedule 17.03.2010
comment
Мне нравится функция свободного формата, и я действительно отредактировал свой пост. Семантика вызова мне до сих пор не ясна. Но я думаю, что это должно быть выполнимо, как я предложил? - person Ayman; 17.03.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;

}
person chub    schedule 17.03.2010
comment
У меня были некоторые проблемы с повышением с моим компилятором (нестандартным и в устаревшей системе). - person Ayman; 17.03.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;
}

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

person Matthieu M.    schedule 17.03.2010
comment
Я действительно ценю время и усилия, которые вы вложили в это. Однако это намного сложнее, чем нужно, и не соответствует Требованию. Наверное, я должен был это сказать. То, что я привел, было примером, и ваш пост очень привязан к этому конкретному примеру. Что на самом деле необходимо, так это простой форматировщик общего назначения для любого типа или строки. Я написал средство форматирования примерно в 10 строках кода. Он делает именно то, что мне нужно. То, что я хочу и просил здесь, - это простой способ вызвать его, который интуитивно понятен для пользователя «API». - person Ayman; 18.03.2010
comment
О, не беспокойтесь об усилиях, я отвечаю только на те вопросы, которые мне интересны! Я хотел бы посмотреть, какое решение вы выбрали, не могли бы вы опубликовать код? - person Matthieu M.; 18.03.2010

Вот первоначальное решение того, что я сделал. Единственная проблема в том, что я до сих пор не могу его шаблонизировать. Если я это сделаю, то вызов программы форматирования изображений будет выглядеть как 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;
}
person Ayman    schedule 18.03.2010