Вопрос оператора C ++ - PullRequest
       11

Вопрос оператора C ++

10 голосов
/ 04 марта 2010

Полагаю, это может быть простой вопрос для всех гуру, но я почему-то не мог найти ответ.

Я хочу, чтобы можно было записывать в CSV-ячейки поток так же просто, как это:

stream << 1 << 2 << "Tom" << std::endl;

, который создаст вывод, как 1,2, Том. Как я могу этого достичь? Я подумал, что мне нужно создать собственный streambuf (так как я не думаю, что это правильный способ сделать это на уровне потока, было бы очень больно просто перегружать << для всех типов), но я не уверен, как <обычно выполняется. Это называется поставить или написать или как. Должен ли я переопределить те или что? Или я просто что-то упустил полностью? </p>

Буду признателен за любую помощь:)

Приветствия

Ответы [ 3 ]

10 голосов
/ 04 марта 2010

Получить что-то вроде 98% пути не так уж сложно:

#include <iostream>

class add_comma { 
    std::ostream &os;
    bool begin;
    typedef add_comma &ref;
public:
    add_comma(std::ostream &o) : os(o), begin(true) {}

    template <class T>
    ref operator<<(T const &t) { 
        if (!begin)
            os << ",";
        os << "\"" << t << "\"";
        begin = false;
        return *this;
    }

    ref operator<<(std::ostream &manip(std::ostream &o) ) {
        if (&manip == &std::endl)
            reset();
        manip(os);
        return *this;
    }

    void reset() { begin = true; }

    operator void *() { return (void *)os; }
};

int main() { 
    add_comma a(std::cout);

    a << 1 << 2 << "This is a string" << std::endl;
    a << 3 << 4 << "Another string" << std::endl;
    return 0;
}

Редактировать: я исправил код хотя бы до некоторой степени - теперь он ставит запятые только между записанными элементами, а не в начале строки. Однако он только распознает «endl» как сигнал о начале новой записи - новая строка в строковом литерале, например, не будет работать.

5 голосов
/ 04 марта 2010

Хотя я могу оценить идею перегрузки потокового оператора, я бы поставил под сомнение практику для рассматриваемой проблемы.

1. Объектно-ориентированный подход

Если вы хотите записать в файл .csv, тогда каждая строка должна иметь тот же формат, что и остальные? К сожалению, ваш оператор потока не проверяет это.

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

Допустим, что (например) вы хотите вывести 2 целых числа и строку:

class Line
{
public:
  Line(int foo, int bar, std::string firstName):
    mFoo(foo), mBar(bar), mFirstName(firstName)

  friend std::ostream& operator<<(std::ostream& out, const Line& line)
  {
    return out << line.mFoo << ',' << line.mBar << ','
               << line.mFirstName << std::endl;
  }
private:
  int mFoo;
  int mBar;
  std::string mFirstName;
};

И использовать его остается очень просто:

std::cout << Line(1,3,"Tom") << Line(2,4,"John") << Line(3,5,"Edward");

2. Хотите повеселиться?

Теперь, это может показаться скучным, и вы можете захотеть поиграть, но все же иметь некоторый контроль над тем, что написано ... хорошо, позвольте мне ввести шаблонное метапрограммирование в драку;)

Вот предполагаемое использование:

// Yeah, I could wrap this mpl_::vector bit... but it takes some work!
typedef CsvWriter< mpl_::vector<int,int,std::string> > csv_type;

csv_type(std::cout) << 1 << 3 << "Tom" << 2 << 4 << "John" << 3 << 5 << "Edward";

csv_type(std::cout) << 1 << 2 << 3; // Compile Time Error:
                                    // 3 is not convertible to std::string

Теперь это было бы интересно, верно? Это отформатировало бы строку и обеспечило бы меру проверки ... Можно всегда усложнять проект, чтобы он делал больше (например, регистрировать валидаторы для каждого поля или для всей строки и т. Д.), Но это уже достаточно сложно.

// namespace mpl_ = boost::mpl

/// Sequence: MPL sequence
/// pos: mpl_::size_t<N>, position in the Sequence

namespace result_of {
  template <class Sequence, class pos> struct operator_in;
}

template < class Sequence, class pos = mpl_::size_t<0> >
class CsvWriter
{
public:
  typedef typename mpl_::at<Sequence,pos>::type current_type;
  typedef typename boost::call_traits<current_type>::param_type param_type;

  CsvWriter(std::ostream& out): mOut(out) {}

  typename result_of::operator_in<Sequence,pos>::type
  operator<<(param_type item)
  {
    typedef typename result_of::operator_in<Sequence,pos>::type result_type;

    if (pos::value != 0) mOut << ',';
    mOut << item;

    if (result_type::is_last_type::value) mOut << std::endl;              

    return result_type(mOut);
  }

private:
  std::ostream& mOut;
}; // class CsvWriter


/// Lil' bit of black magic
namespace result_of { // thanks Boost for the tip ;)

  template <class Sequence, class pos>
  struct operator_in
  {
    typedef typename boost::same_type<
        typename mpl_::size<Sequence>::type,
        typename mpl_::next<pos>::type
      > is_last_type;

    typedef typename mpl_::if_<
      is_last_type,
      CsvWriter< Sequence, mpl_::size_t<0> >,
      CsvWriter< Sequence, typename mpl_::next<pos>::type >
    >::type;
  }; // struct operator_in<Sequence,pos>

} // namespace result_of

Здесь у вас есть средство записи потоков, которое обеспечивает правильное форматирование файла cvs ... исключая символы новой строки в строках;)

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

Если A - итератор над элементами ...

copy(A, A + N, ostream_iterator<int>(cout, ","));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...