ШАБЛОНЫ 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 ++. В ожидании вы можете поделиться своим опытом.