Как определить мою функцию регистратора из макросов

У меня есть файл заголовка для регистрации, поступающий из статической библиотеки, у меня нет доступа к исходному коду библиотеки, только к заголовку. Мне нравится их способ отладки, я бы хотел создать свой файл отладки 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 почему?

fmt должен быть спецификатором формата (стиль printf) для переменных аргументов (если его нет, это может быть пустая строка). Возможно, github.com/Kurento/gstreamer/blob/master/gst/gstinfo.h поможет.

CristiFati 31.05.2024 12:08

Подумайте, есть ли какая-нибудь полезная информация в #define макросе для отладочной печати на C?

Jonathan Leffler 02.06.2024 22:39

Трудно понять, почему показанный заголовок не содержит объявления Logger_UserLogOutput(). Также странно, что аргумент "[%s: %d)" передается единообразно — аргументы должны относиться к вещам, которые различаются между вызовами функции. Обозначение , ## __VA_ARGS__ является расширением GCC стандарта C. C23 предоставляет альтернативный, лучший способ справиться с этим — механизм __VA_OPT__(…), который также есть в C++20.

Jonathan Leffler 02.06.2024 22:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это оболочки для функции, подобной 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 31.05.2024 12:30

@simon Как уже упоминалось в этом ответе, fmt — это строка формата. Как и в printf("%s", str), часть "%s" является строкой формата. Передавать это в функцию отладки имеет смысл только в том случае, если вы хотите использовать собственное форматирование и переменное количество аргументов так же, как и printf.

Lundin 31.05.2024 12:57

@simon: Обычно реализация функции журналирования будет использовать vprintf(), vfprintf() или vsprintf(), поскольку аргументы переменных должны быть идентифицированы va_list (см. <stdarg.h>). И зачастую разумнее всего записать отладочную информацию либо в stderr, либо в файл, чтобы вы с меньшей вероятностью использовали vprintf(), чем другие функции.

Jonathan Leffler 02.06.2024 22:56
Ответ принят как подходящий

Вы можете определить свои собственные вариативные функции, используя макросы 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");
}

Другие вопросы по теме