У меня есть файл заголовка для регистрации, поступающий из статической библиотеки, у меня нет доступа к исходному коду библиотеки, только к заголовку. Мне нравится их способ отладки, я бы хотел создать свой файл отладки owm, вдохновленный их способом ведения дел.
Вот заголовочный файл
#ifndef _LOGGER_H
#define _LOGGER_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LOGGER_CONSOLE_LOG_LEVEL_ERROR = 0,
LOGGER_CONSOLE_LOG_LEVEL_WARN = 1,
LOGGER_CONSOLE_LOG_LEVEL_INFO = 2,
LOGGER_CONSOLE_LOG_LEVEL_DEBUG = 3,
} E_LoggerConsoleLogLevel;
#define USER_LOG_DEBUG(fmt, ...) \
Logger_UserLogOutput(LOGGER_CONSOLE_LOG_LEVEL_DEBUG, "[%s:%d) " fmt, __FUNCTION__, __LINE__ , ##__VA_ARGS__)
#define USER_LOG_INFO(fmt, ...) \
Logger_UserLogOutput(LOGGER_CONSOLE_LOG_LEVEL_INFO, "[%s:%d) " fmt, __FUNCTION__, __LINE__ , ##__VA_ARGS__)
#define USER_LOG_WARN(fmt, ...) \
Logger_UserLogOutput(LOGGER_CONSOLE_LOG_LEVEL_WARN, "[%s:%d) " fmt, __FUNCTION__, __LINE__ , ##__VA_ARGS__)
#define USER_LOG_ERROR(fmt, ...) \
Logger_UserLogOutput(LOGGER_CONSOLE_LOG_LEVEL_ERROR, "[%s:%d) " fmt, __FUNCTION__, __LINE__ , ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif
Если я хорошо понимаю, мне нужно объявить свою функцию Logger_UserLogOutput(E_LoggerConsoleLogLevel level,...)но я не знаю, что мне нужно поставить в качестве аргумента levelчто такое fmt?
Например, я хочу сделать что-то подобное USER_LOG_DEBUG("debug msg"); и иметь что-то подобное в своем серийном терминале: [DEBUG] function:line : debug msg
Обновлено: void Logger_UserLogOutput(E_LoggerConsoleLogLevel level,const char *fmt, ...)это мой прототип
но если я попытаюсь напечатать значение, например:
printf("[DEBUG] %s\n"); У меня всегда было [DEBUG] [%s:%d) debug msg почему?
Подумайте, есть ли какая-нибудь полезная информация в #define макросе для отладочной печати на C?
Трудно понять, почему показанный заголовок не содержит объявления Logger_UserLogOutput(). Также странно, что аргумент "[%s: %d)" передается единообразно — аргументы должны относиться к вещам, которые различаются между вызовами функции. Обозначение , ## __VA_ARGS__ является расширением GCC стандарта C. C23 предоставляет альтернативный, лучший способ справиться с этим — механизм __VA_OPT__(…), который также есть в C++20.





Это оболочки для функции, подобной printf, где fmt — это строка формата, и они также допускают переменное количество аргументов, например printf. Поэтому, если вы хотите написать функцию, принимающую этот ввод, она должна быть вариативной функцией с функциями stdarg.h (вариативные функции обычно не рекомендуются).
Эти макросы предполагают, что fmt является строковым литералом, и объединяют его со строковым литералом "[%s:%d) ". Кроме того, ##__VA_ARGS__ — это нестандартное расширение компилятора, позволяющее поглощать пустые списки аргументов — это не стандарт C.
__FUNCTION__ тоже не является стандартным C, но остался старый GNU C 1990-х годов. Начиная с C99, мы можем использовать ISO C __func__, вот что нам следует делать.
Если вам не нужна вся эта несколько перегруженная гибкость, а просто печатать строки, вы можете просто сделать облегченную версию:
#define USER_LOG_ERROR(str) printf("%s %s() line %d: %s\n", __FILE__, __func__, __LINE__, str)
Пример:
#include <stdio.h>
#define USER_LOG_ERROR(str) printf("%s %s() line %d: %s\n", __FILE__, __func__, __LINE__, str)
int main (void)
{
USER_LOG_ERROR("bad stuff happened");
}
Выход:
/app/example.c main() line 7: bad stuff happened
Хорошо, спасибо @Lundin, и если я хочу выполнить printf в своей функции Logger_UserLogOutput с этим прототипом Logger_UserLogOutput(E_LoggerConsoleLogLevel level, const char *fmt, ...), это возможно? Стоит ли мне делать printf("%s", fmt)?
@simon Как уже упоминалось в этом ответе, fmt — это строка формата. Как и в printf("%s", str), часть "%s" является строкой формата. Передавать это в функцию отладки имеет смысл только в том случае, если вы хотите использовать собственное форматирование и переменное количество аргументов так же, как и printf.
@simon: Обычно реализация функции журналирования будет использовать vprintf(), vfprintf() или vsprintf(), поскольку аргументы переменных должны быть идентифицированы va_list (см. <stdarg.h>). И зачастую разумнее всего записать отладочную информацию либо в stderr, либо в файл, чтобы вы с меньшей вероятностью использовали vprintf(), чем другие функции.
Вы можете определить свои собственные вариативные функции, используя макросы va_ из stdarg.h. Вы также можете передать список аргументов с переменным числом аргументов в такие функции, как vprintf, чтобы напечатать строку формата. Пример:
#include <stdarg.h>
void Logger_UserLogOutput(E_LoggerConsoleLogLevel level, const char *fmt, ...) {
va_list args;
if (level == LOGGER_CONSOLE_LOG_LEVEL_DEBUG) {
printf("[DEBUG] ");
}
// code for other levels…
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
printf("\n");
}
fmt должен быть спецификатором формата (стиль printf) для переменных аргументов (если его нет, это может быть пустая строка). Возможно, github.com/Kurento/gstreamer/blob/master/gst/gstinfo.h поможет.