Пользовательский поток
Мне нужно руководство или указатели, которые понимают, как реализовать пользовательский поток. Мои требования:
- Класс с '< < оператора для нескольких типов данных.
- Цель состоит в отправке вывода в базу данных. Каждая "строка" должна перейти к отдельной записи.
- В каждой записи наиболее важным полем будет текст (или blob), но некоторые другие поля, такие как время и т.д., могут быть в основном выведены автоматически.
Буферизация
- важна, поскольку я не хочу обращаться к базе данных для каждой записи.
Во-первых, стоит ли это извлекать из ostream? Что я получаю от вывода из потока? Что делать, если мой класс просто реализует несколько методов operator<<
(включая некоторые пользовательские типы данных). Какую функциональность я получаю из потока?
Предполагая, что я хочу, это класс, полученный из ostream, мне нужно некоторое руководство, понимающее взаимосвязь между классами ostream и streambuf. Какой из них мне нужно реализовать? Если посмотреть на некоторые образцы, кажется, что мне вообще не нужно выводить из ostream, а просто дать конструктору ostream собственный streambuf. Это правда? что канонический подход?
Какие виртуальные функции в пользовательском streambuf мне нужно реализовать? Я видел несколько примеров (включая этот сайт: здесь и здесь и еще несколько), некоторые переопределяют метод sync
и другие переопределяют метод overflow
. Какой я должен переопределить? Кроме того, глядя на источники stringbuf и filebuf (Visual Studio или GCC), оба эти класса буферов реализуют множество методов streambuf.
Если требуется собственный класс, созданный из streambuf, будет ли какая-либо выгода, получаемая из stringbuf (или любого другого класса), а не непосредственно из streambuf?
Что касается "строк". Я бы хотел, по крайней мере, когда мои пользователи класса с использованием манипулятора 'endl' стали новой строкой (т.е. Записью в базе данных). Может быть - зависит от усилий - каждый символ \n 'следует рассматривать как новую запись. Кто из моих пользовательских ostream и/или streambuf получает уведомление для каждого?
Ответы
Ответ 1
Пользовательское назначение для ostream означает реализацию вашего собственного ostreambuf. Если вы хотите, чтобы ваш streambuf фактически буферизовался (т.е. Не подключался к базе данных после каждого символа), самый простой способ сделать это - создать класс, наследующий от std::stringbuf
. Функция только, которую вам нужно переопределить, это метод sync()
, который вызывается всякий раз, когда поток очищается.
class MyBuf : public std::stringbuf
{
public:
virtual int sync() {
// add this->str() to database here
// (optionally clear buffer afterwards)
}
};
Затем вы можете создать std::ostream
с помощью своего буфера:
MyBuf buff;
std::ostream stream(&buf)
Большинство людей советовали не перенаправлять поток в базу данных, но они проигнорировали мое описание, что в базе данных в основном есть одно поле blob, где весь текст будет.
В редких случаях я могу отправить данные в другое поле. Этому могут способствовать пользовательские атрибуты, понятные моему потоку. Например:
MyStream << "Some text " << process_id(1234) << "more text" << std::flush
В приведенном выше коде будет создана запись в базе данных с помощью:
blob: 'Some text more text'
process_id: 1234
process_id()
- метод, возвращающий структуру ProcessID
. Затем, в реализации моего ostream, у меня есть operator<<(ProcessID const& pid)
, который хранит идентификатор процесса до тех пор, пока он не будет написан. Отлично работает!
Ответ 2
Самый простой способ - наследовать std::streambuf
и переопределить только два метода:
-
std::streamsize xsputn(const char_type* s, std::streamsize n)
- добавить данный буфер с размером, указанным в вашем внутреннем буфере, std::string
например;
-
int_type overflow(int_type c)
- добавить один char
в свой внутренний буфер.
Ваш streambuf может быть сконструирован из всего, что вам нужно (например, для подключения к базе данных). После добавления чего-либо во внутренний буфер вы можете попытаться разбить его на строки и нажать что-нибудь в БД (или просто буферизовать SQL-запросы для выполнения позже).
Чтобы использовать его: просто прикрепите streambuf
к любому std::ostream
с помощью конструктора.
Simple! Я сделал что-то подобное для вывода строк в syslog - все работает отлично с любым пользовательским operator<<
для пользовательских классов.
Ответ 3
my2c - Я думаю, что вы занимаетесь этим неправильно. Поток может показаться приятной идеей, но вам понадобится способ указать конец строки тоже (а потом что, если кто-то забудет?) Я бы предложил что-то вроде того, как работают Java PreparedStatements и партии, так как в предоставлении набора методов, которые принимают типы и индекс столбца, затем "пакетный" метод, который явно дает понять, что вы действительно выполняете эту строку, а затем выполняете, чтобы вставить пакет.
Любая операция, основанная на потоке, будет полагаться на тип (обычно), чтобы указать, какой столбец заполняется, но что, если у вас есть два ints? IMO, как пользователь, не кажется естественным способом вставки записей в базу данных...
Ответ 4
Чтобы добавить новый источник или назначение ввода/вывода символов в механизм iostreams, вы должны создать новый класс streambuf
. Задача классов буфера потока - связываться с "внешним устройством", которое будет хранить символы и предоставлять средства буферизации.
Проблема с использованием iostreams для связи с вашей базой данных заключается в том, что таблица базы данных не соответствует концепции последовательности символов. Немного похоже на толкание круглого штифта в квадратное отверстие. A streambuf
работает только с символами. Это единственное, что когда-либо было представлено ему. Это означает, что streambuf
должен проанализировать переданный ему поток символов, чтобы найти разделители полей и записей.
Если вы решите пойти по этому маршруту, я предскажу, что вы в конце концов напишите конвертер CSV-to-SQL в своем streambuf
, чтобы заставить его работать.
Вероятно, вам будет лучше, только добавив несколько перегрузок operator<<
к вашим классам. Вы можете посмотреть на рамки Qt для идей здесь. Они также имеют возможность использовать operator<<
для добавления элементов в коллекции и т.д.