Буферизация

<!--StartFragment -->

При задании операций ввода/вывода мы никак не касались типов файлов, но ведь не все устройства можно рассматривать одинаково с точки зрения стратегии буферизации. Например, для ostream, подключенного к символьной строке, требуется буферизация другого вида, нежели для ostream, подключенного к файлу. С этими проблемами можно справиться, задавая различные буферные типы для разных потоков в момент инициализации (обратите внимание на три конструктора класса ostream). Есть только один набор операций над этими буферными типами, поэтому в функциях ostream нет кода, их различающего. Однако функции, которые обрабатывают переполнение сверху и снизу, виртуальные. Этого достаточно, чтобы справляться с необходимой в данное время стратегией буферизации. Это также служит хорошим примером применения виртуальных функций для того, чтобы сделать возможной однородную обработку логически эквивалентных средств с различной реализацией. Описание буфера потока в выглядит так:


struct streambuf { // управление буфером потока


char* base; // начало буфера
char* pptr; // следующий свободный char
char* qptr; // следующий заполненный char
char* eptr; // один из концов буфера
char alloc; // буфер, выделенный с помощью new


// Опустошает буфер:
// Возвращает EOF при ошибке и 0 в случае успеха
virtual int overflow(int c =EOF);


// Заполняет буфер
// Возвращет EOF при ошибке или конце ввода,
// иначе следующий char
virtual int underflow();


int snextc() // берет следующий char
{
return (++qptr==pptr) ? underflow() : *qptr&0377;
}


// ...


int allocate() // выделяет некоторое пространство буфера


streambuf() { /* ... */}
streambuf(char* p, int l) { /* ... */}
~streambuf() { /* ... */}
};


Обратите внимание, что здесь определяются указатели, необходимые для работы с буфером, поэтому обычные посимвольные действия можно определить (только один раз) в виде максимально эффективных inline- функций. Для каждой конкретной стратегии буферизации необходимо определять только функции переполнения overflow() и underflow(). Например:


struct filebuf : public streambuf {


int fd; // дескриптор файла
char opened; // файл открыт


int overflow(int c =EOF);
int underflow();


// ...


// Открывает файл:
// если не срабатывает, то возвращает 0,
// в случае успеха возвращает \"this\"
filebuf* open(char *name, open_mode om);
int close() { /* ... */ }


filebuf() { opened = 0; }
filebuf(int nfd) { /* ... */ }
filebuf(int nfd, char* p, int l) : (p,l) { /* ... */ }
~filebuf() { close(); }
};


int filebuf::underflow() // заполняет буфер из fd
{
if (!opened || allocate()==EOF) return EOF;


int count = read(fd, base, eptr-base);
if (count < 1) return EOF;


qptr = base;
pptr = base + count;
return *qptr & 0377;
}



Опубликовал admin
23 Мар, Вторник 2004г.



Программирование для чайников.