У меня возникли проблемы при попытке реализовать пользовательский класс потока для создания кода с хорошим отступом в выходном файле. Я много искал в Интернете, но, похоже, нет единого мнения о том, как лучше всего этого добиться. Некоторые люди говорят о получении потока, другие говорят о получении буфера, а другие предлагают использовать локали / аспекты и т. Д.
По сути, я нахожу, что пишу много кода, подобного этому:
ofstream myFile();
myFile.open("test.php");
myFile << "<html>" << endl <<
"\t<head>" << endl <<
"\t\t<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» записывается в файл. Нужно ли перегружать << оператор для моего нового класса? (Я не смог найти способ сделать это, который строит)
Спасибо!
постскриптум
1) Я опустил #include, используя пространство имен и т. Д. Из моих фрагментов кода для экономии места.
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.
Мне пришлось сделать что-то неприятное, поэтому мне интересно услышать отзывы об утилите кодов.