На этом шаге мы продолжим изучение этого вопроса.
Чтобы конструировать потоковые буферы было проще, рекомендуется также реализовать специальный класс потока данных, назначение которого сводится к передаче аргументов конструктора соответствующему потоковому буферу. В следующем примере показано, как это делается. В данном случае определяется класс потокового буфера, который инициализируется файловым дескриптором, используемым для записи символов функцией write() (низкоуровневая функция ввода-вывода в системах семейства UNIX). Также определяется класс, производный от ostream, - он поддерживает потоковый буфер, которому передается файловый дескриптор.
// oubuf2.hpp #include <iostream> #include <streambuf> #include <cstdio> extern "C" { int write (int fd, const char* buf, int num); } class fdoutbuf : public std::streambuf { protected: int fd; // Файловый дескриптор public: // Конструктор fdoutbuf (int _fd) : fd(_fd) { } protected: // Запись одного символа virtual int_type overflow (int_type c) { if (c != EOF) { char z = c; if (write (fd, &z, 1) != 1) { return EOF; } } return c; } // Запись нескольких символов virtual std::streamsize xsputn (const char* s, std::streamsize num) { return write(fd,s,num); } }; class fdostream : public std::ostream { protected: fdoutbuf buf; public: fdostream (int fd) : std::ostream(0), buf(fd) { rdbuf(&buf); } };
Потоковый буфер также реализует функцию xsputn(), позволяющую предотвратить вызов overflow() для каждого символа при отправке в буфер последовательности символов. Функция записывает всю последовательность символов в файл, заданный дескриптором fd, за один вызов. Функция xsputn() возвращает количество успешно выведенных символов. Пример:
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include "outbuf2.hpp" #include <conio.h> //необходимо для getch() #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) { char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; } int main (int argc, char* argv[]) { fdostream out(1); // Поток с буфером, записывающим по дескриптору 1 out << ToRus("31 в шестнадцатеричной системе счисления: ") << std::hex << 31 << std::endl; getch(); return 0; } //---------------------------------------------------------------------------
Результат работы приложения выглядит так:
Рис.1. Результат работы приложения
Программа создает выходной поток данных, инициализируемый дескриптором файла 1. По действующим правилам этот дескриптор соответствует стандартному каналу вывода. Следовательно, в данном примере символы будут просто направляться в стандартный выходной поток данных. В аргументе конструктора также могут использоваться другие дескрипторы (например, дескриптор файла или сокета).
На следующем шаге мы закончим изучение этого вопроса.