ШАБЛОНЫ C ++
C ++: современный регистратор с библиотеками plog и fmt :: format
Работайте с ведением журнала и форматированием текста максимально эффективно
Добро пожаловать в четвертый выпуск практических решений C ++. Если вы читали предыдущие три статьи, то наверняка заметили, что я сосредотачиваюсь на дизайне контейнеров. Давайте ненадолго отойдем в сторону и поговорим об одной из самых важных частей больших программных проектов. Это журнал состояния программного обеспечения. Зачем это нужно? Он позволяет отслеживать все действия пользователя в целях безопасности или для воссоздания сценария. И это действительно полезно для отслеживания ошибок, если настроен соответствующий уровень трассировки. Я думаю, что даже этих причин достаточно, чтобы сделать процесс регистрации максимально быстрым и простым.
Во-первых, я хочу представить две замечательные библиотеки: fmtlib и plog.
plog - небольшая, но мощная библиотека регистрации только для заголовков. Он обеспечивает минималистичную структуру для создания пользовательских регистраторов, поэтому вам не нужно изобретать колесо в поисках идеальных приборов для регистрации. Он кроссплатформенный, поточно-ориентированный, переносимый, достаточно проверить его документацию и тестовые примеры.
С другой стороны, fmtlib аккумулирует набор инструментов для размещения строк, форматирования, объединения и печати (включая стиль Pythonic). Все эти операции имеют не только безопасную для памяти реализацию, но и являются самыми быстрыми среди всех аналогов. Не говоря уже о колоссальном количестве функций, которые делают ваш код аккуратным, быстрым и красивым. Итак, взгляните на это и проверьте документацию на наличие доступных API.
Итак, похоже, что эти библиотеки были созданы для совместного использования. Давайте внимательно посмотрим на идеальный симбиоз!
1. Подготовьте контейнер
Конечно, ведение журнала и форматирование не могут существовать отдельно от практического примера. В демонстрационных целях c мы будем использовать скелет, похожий на контейнер (просто потому, что контейнеры прекрасны, и я не вижу причин избегать их). Давайте сделаем это просто и построим код на следующих принципах:
- создать минималистичный и аккуратный интерфейс;
- пусть наш контейнер сделает очень простую работу: просто сохраните числа и посчитайте их;
- построить реализацию единого регистратора. Конечно, мы можем предоставить идиому PIMPL, но давайте будем простыми;
- держите реализацию регистратора отдельно от контейнера.
Хорошо, теперь мы можем подготовить код на основе этих принципов:
Как видите - все просто и глупо, как мы и хотели. Теперь мы можем сосредоточиться на самом важном.
2. Конструкция регистратора
Следующим шагом будет разработка логгера. Вот список функций, которые (как я думаю) покажут большой потенциал библиотек:
- реализация логгера зависит от типа контейнера, но контейнер не зависит от логгера;
- форматтер и сам регистратор разделены, поэтому каждую часть можно изменять независимо;
- у регистратора есть собственный экземпляр, независимый от основной программы (так что при необходимости plog можно использовать повторно);
- информация передается как в файл, так и в консоль.
3. Один - регистратор, два - регистратор.
Теперь нам нужно создать реализацию по предыдущему плану. Дизайн фреймворка plog поможет нам. Как указано в библиотеке, регистратор состоит из приложения, средства форматирования и конвертера. Appender записывает сообщение в желаемый источник (консоль, хранилище, файл и т. Д.). Formatter создает сообщение в нужной нам форме и формате на основе переданных данных. Конвертер дает дополнительный уровень форматирования (мы не будем использовать его в нашем примере). Итак, начнем с аппендера.
Фактически у нас будет два приложения: первое для входа в файл, а второе для входа в консоль. И удобно, что для каждой из этих реализаций нужен только короткий шаблон с одной перегрузкой метода. Кроме того, мы будем соблюдать стандарты plog и вызывать программу форматирования перед регистрацией. Начинаем вход в файл:
Войти в консоль еще проще. Кроме того, это идеальное место для использования функции fmtlib - альтернативного вывода в консоль. Как было сказано - это безопасно и намного быстрее стандартного.
И это все, у нас есть инструменты для занесения логов в нужные источники.
4. Больше шаблонов
Следующим шагом является привязка регистратора к типу данных контейнера. Другими словами, мы инкапсулируем синтаксический анализ и форматирование типов данных. После этого мы будем использовать одну строку кода для создания журнала. Нам нужны два новых шаблона: один для регистратора, чтобы понимать тип контейнера, и один для функций fmtlib, инкапсулирующих форматирование этого типа данных. Начнем с шаблона форматирования:
На самом деле мы обманули и добавили публичный метод Format()
(и еще несколько функций) в наш класс:
Тем не менее, это сделано только для упрощения. В другом случае нам пришлось добавить к нашему контейнеру множество общедоступных методов и перенести всю логику форматирования в новый шаблон. Теперь нам нужна единственная строка для создания строки на основе контейнера:
MyPreciousContainer cont auto str = fmt::format(FMT_STRING(“{}\n”), cont)
Итак, мы используем эту строку в шаблоне оператора потока регистратора:
После этого кода мы используем одну строку для создания журнала, например:
MyPreciousContainer cont PLOG_INFO << cont;
5. Добавьте форматирование
Фактически, мы реализовали желаемые компоненты для всех этапов ведения журнала: передать запись регистратору, отформатировать запись в строку и поместить эту строку в место назначения. Хотя было бы очень полезно добавить больше деталей (например, время регистрации, серьезность и т. Д.) И иметь возможность изменять кадрирование. В этом нам поможет еще одна функция plog - форматтер.
Это простой шаблон, который добавляет рамку вокруг строки журнала перед выводом.
Как вы помните, в аппендере используется метод format
.
Теперь мы готовы к тестированию.
6. Регистратор готов
Последний шаг - запустить регистраторы и добавить фактический вызов регистратора в код контейнера:
И получаются красивые строки вроде:
2020–04–26, 13:41:40.875 INFO Instance : 1. Element was deleted. Current size: 0. Current operation number: 6.
Итак, наша миссия выполнена: в контейнере появился логгер, который может записывать форматированные сообщения в разные источники, поддерживает принципы ООП и может быть легко заменен.
Я увлечен C ++ и разработкой программного обеспечения, и за свою карьеру я накопил много интересных приемов и решений. Неудивительно, что я хочу ими поделиться. Поэтому пишу этот цикл статей. Если вас интересует C ++ и практические решения - добро пожаловать в мой Github, где находится полный рабочий пример.
Также убедитесь, что вы не пропустили предыдущий выпуск:
Следите за появлением следующих выпусков проектов C ++. В ожидании вы можете поделиться своим опытом.