Специализированный макрос с аргументами - PullRequest
1 голос
/ 09 апреля 2020

Предположим, у меня есть следующий макрос:

#define LOG(L, ...) func(L, __VA_ARGS__);

Где L может быть одним из INFO, WARN, FATAL

Теперь я хочу определить его для FATAL по-другому.

#define LOG(FATAL, ...) {func(FATAL, __VA_ARGS__); exit(-1);}

Как выполнить sh это?

Редактировать: Как следует из вышесказанного, есть ли лучший способ сделать это? например, избегая макросов.

Ответы [ 2 ]

4 голосов
/ 09 апреля 2020

Макросы - это, в основном, плохой выбор в C ++, в основном из-за того, что они имеют независимость от пространства имен c и могут вступать в силу там, где это неожиданно.

Тем не менее, пример того, как можно решить проблему OP, например используя вставку токена :

#include <iostream>

#define LOG(LEVEL, ...) LOG_##LEVEL(__VA_ARGS__)

#define LOG_INFO(...) log(Info, __VA_ARGS__)
#define LOG_WARN(...) log(Warn, __VA_ARGS__)
#define LOG_FATAL(...) do { log(Error, __VA_ARGS__); std::cerr << "Program aborted!\n"; } while (false)

enum Level { Info, Warn, Error };

void log(Level level, const char *text)
{
  static const char *levelText[] = { "INFO", "WARNING", "ERROR" };
  std::cerr << levelText[level] << ": " << text << '\n';
}

int main()
{
  LOG(INFO, "Everything fine. :-)");
  LOG(WARN, "Not so fine anymore. :-|");
  LOG(FATAL, "Things became worst. :-(");
}

Вывод:

INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became worst. :-(
Program aborted!

Живая демонстрация на coliru


Еще один пример для последующего вопроса - с шаблонами variadi c вместо макросов:

#include <iostream>

enum Level { Info, Warn, Error, Fatal };

template <typename ...ARGS>
void log(Level level, ARGS&& ... args)
{
  static const char *levelText[] = { "INFO", "WARNING", "ERROR", "FATAL" };
  std::cerr << levelText[level] << ": ";
  (std::cerr << ... << args);
  std::cerr << '\n';
  if (level == Fatal) std::cerr << "Program aborted!";
}

int main()
{
  log(Info, "Everything fine.", ' ', ":-)");
  log(Warn, "Not so fine anymore.", ' ', ":-|");
  log(Error, "Things became bad.", ' ', ":-(");
  log(Fatal, "Things became worst.", ' ', "XXX");
}

Вывод:

INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became bad. :-(
FATAL: Things became worst. XXX
Program aborted!

Демонстрация в реальном времени на coliru

Должен признать, что мне понадобилась помощь для распаковки параметров - можно найти здесь: SO: Какой самый простой способ напечатать пакет параметров c с использованием std :: ostream? .

2 голосов
/ 09 апреля 2020

Расширяющий Scheffs отвечает немного, этот передает уровень журнала как аргумент шаблона, позволяя использовать if constexpr (как JVApen упомянул в своем комментарии). Вам нужно знать соответствующий уровень для каждого вывода журнала во время компиляции, но я думаю, что это не будет проблемой.

enum Level { Info, Warn, Error, Fatal };

template<Level L, typename ...ARGS>
void log(ARGS&& ... args)
{
    static const char *levelText[] = { "INFO", "WARNING", "ERROR", "FATAL" };
    std::cerr << levelText[L] << ": ";
    (std::cerr << ... << args);
    std::cerr << '\n';
    if constexpr (L == Fatal)
        std::cerr << "Program aborted\n";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...