пользовательский выходной поток c++ с отступом


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

По сути, я обнаружил, что пишу много кода, подобного этому:

ofstream myFile();
myFile.open("test.php");
myFile << "<html>" << endl <<
          "t<head>" << endl <<
          "tt<title>Hello world</title>" << endl <<
          "t</head>" << endl <<
          "</html>" << endl;

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

ind_ofstream myFile();
myFile.open("test.php");
myFile << "<html>" << ind_inc << ind_endl <<
          "<head>" << ind_inc << ind_endl <<
          "<title>Hello world</title>" << ind_dec << ind_endl <<
          "</head>" << ind_dec << ind_endl <<
          "</html>" << ind_endl;

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

Итак, вот мой шанс реализовать класс & манипуляторы:

Ind_ofstream.h

class ind_ofstream : public ofstream
{
    public:
        ind_ofstream();
        void incInd();
        void decInd();
        size_t getInd();

    private:
        size_t _ind;
};

ind_ofstream& inc_ind(ind_ofstream& is);
ind_ofstream& dec_ind(ind_ofstream& is);
ind_ofstream& endl_ind(ind_ofstream& is);

Ind_ofstream.cpp

ind_ofstream::ind_ofstream() : ofstream()   {_ind = 0;}
void ind_ofstream::incInd()     {_ind++;}
void ind_ofstream::decInd()     {if(_ind > 0 ) _ind--;}
size_t ind_ofstream::getInd()       {return _ind;}

ind_ofstream& inc_ind(ind_ofstream& is)     
{ 
    is.incInd();
    return is; 
}

ind_ofstream& dec_ind(ind_ofstream& is)     
{ 
    is.decInd();
    return is; 
}

ind_ofstream& endl_ind(ind_ofstream& is)    
{
    size_t i = is.getInd();
    is << endl;
    while(i-- > 0) is << "t";
    return is;
}

Это строит, но не генерирует ожидаемый результат; любая попытка использовать пользовательские манипуляторы приводит к тому, что они по какой-то причине преобразуются в логическое значение и записываются в файл "1". Нужно ли перегружать оператор

Спасибо!

P. s.

1) я опустил #includes, используя пространство имен etc из моих фрагментов кода, чтобы сэкономить место.

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

2 9

2 ответа:

Iostreams поддерживают добавление пользовательских данных в них, поэтому вам не нужно писать полный производный класс только для добавления уровня отступа, который будет управляться манипуляторами. Это малоизвестная особенность iostreams, но здесь пригодится.

Вы бы написали свои манипуляторы так:

/* Helper function to get a storage index in a stream */
int get_indent_index() {
    /* ios_base::xalloc allocates indices for custom-storage locations. These indices are valid for all streams */
    static int index = ios_base::xalloc();
    return index;
}

ios_base& inc_ind(ios_base& stream) {
    /* The iword(index) function gives a reference to the index-th custom storage location as a integer */
    stream.iword(get_indent_index())++;
    return stream;
}

ios_base& dec_ind(ios_base& stream) {
    /* The iword(index) function gives a reference to the index-th custom storage location as a integer */
    stream.iword(get_indent_index())--;
    return stream;
}

template<class charT, class traits>
basic_ostream<charT, traits>& endl_ind(basic_ostream<charT, traits>& stream) {
    int indent = stream.iword(get_indent_index());
    stream.put(stream.widen('\n');
    while (indent) {
        stream.put(stream.widen('\t');
        indent--;
    }
    stream.flush();
    return stream;
}

Я объединил решение Барта Ван Ингена Шенау с фасеткой, чтобы позволить подталкивать и выскакивать уровни отступов к существующим выходным потокам. Код доступен на github: https://github.com/spacemoose/ostream_indenter , и есть более тщательная демонстрация/тест в репозитории, но в основном это позволяет вам сделать следующее:

/// This probably has to be called once for every program:
// http://stackoverflow.com/questions/26387054/how-can-i-use-stdimbue-to-set-the-locale-for-stdwcout
std::ios_base::sync_with_stdio(false);

std::cout << "I want to push indentation levels:\n" << indent_manip::push
          << "To arbitrary depths\n" << indent_manip::push
          << "and pop them\n" << indent_manip::pop
          << "back down\n" << indent_manip::pop
          << "like this.\n" << indent_manip::pop;

Произвести:

I want to push indentation levels:
    To arbitrary depths
        and pop them
    back down
like this.

Я должен был сделать своего рода неприятный трюк, поэтому мне интересно услышать отзывы о утилите codes.