2016-05-20 1 views
11

Приложение имеет систему ведения журнала, которая позволяет включать или отключать функции ведения журнала своих модулей во время выполнения. Команды журнала принимают потоки на входе (что является безопасной альтернативой «sprintf», вряд ли существует более неприятная ситуация, чем если причиной является ваша система отладки.Короткое замыкание потока

Проблема в том, что если я выполняю такие вещи, как:

logger.Trace << "Requests pending:" << buffer.findRequests(); 

и findRequests() обладает высокой вычислительной сложности, даже если отключить уровень журнала трассировки для модуля, поиск будет выполняться (при сборке потока), прежде чем он отклонен внутри метода трассировки operator<<.

очевидный альтернативой было бы засорять код:

if(logger.Trace.Enabled()) logger.Trace << ... 

Это некрасиво, и это не удобно. Я мог бы заменить его с помощью макроса с помощью if, или тот, который использует && короткое замыкание, будучи несколько лучше (может быть использована в качестве RValue, который следующий поток философия возвращает BOOL ложны на инвалидах потока):

#define TRACE if(logger.Trace.Enabled()) logger.Trace 

    #define TRACE dummyLogVar = logger.Trace.Enabled() && logger.Trace 

Ни в особенно красивой или безопасной. Коллега предложил закрытия:

logger.Trace([&](f){f << "Requests pending:" << buffer.findRequests();}); 

.Trace оценки будет закрытие, только если этот уровень включен. Логично, это приятно, но синтаксически абсолютно ужасно. Набрав этот беспорядок: logger.Trace([&](f){f << ... ;}); сотни раз?

Есть ли более аккуратный, безопасный и удобный способ предотвращения оценки потока?

+0

Я думаю, что закрытие - это единственный способ. Я должен был решить эту же проблему до введения замыканий, и я снова вернулся к макросам и вызовам функций в стиле C. –

+2

_ «Логично, это приятно, но синтаксически абсолютно ужасно». Хе-хе приветствую C++ –

+0

@LightnessRacesinOrbit: Будьте счастливы, я не использовал '<:&:>' для закрытия. Наткнулся на этот вчерашний день, озадачил меня на некоторое время. В действительности '<:' является орграфом для '['. Кто в наши дни все еще использует орграфы и почему? И почему в мире C++ 11 расширил набор орграфов и триграфов ?! Кто-то неправильно понял «встроенное программирование» и использует телефон Android с сенсорной клавиатурой для написания приложений для ПК? –

ответ

8

Макросы действительно могут быть использованы, но вам нужны функциональные макросы, которые выводят вывод в качестве аргумента, и вам нужно убедиться, что это один оператор.

Вторая часть, убедившись, что тело макроса является одним из утверждений, легко и обычно выполняется с использованием do { ... } while (false).

Использование аргумента макроса также не сложно, если в аргументе нет запятых. Ограничение запятой включает в себя использование вызовов функций в макро-аргументе с его собственными аргументами, препроцессор довольно глупый, любой использует любой запятой в аргументе макроса как разделитель аргументов для макроса.

В своей простейшей форме, не беспокоя с запятой самоограничения, макрос может выглядеть примерно как

#define TRACE(output)     \ 
    do         \ 
    {         \ 
     if (logger.Trace.Enabled())  \ 
     {        \ 
      logger.Trace << output;  \ 
     }        \ 
    } while (false) 

Обратите внимание, что нет semicilon после while (false).

Вы затем использовать его как

TRACE("Requests pending:" << buffer.findRequests()); 

do { ... } while (false) часть, скорее всего, будет оптимизирован прочь компилятором, оставив вас с простой if проверки. Если logger.Trace.Enabled() возвращает false, тогда ничего не должно произойти помимо этой проверки.

Если у вас есть компилятор, способный на C++ 11 или более поздний, он должен иметь поддержку variadic macros, которая должна помочь вам преодолеть ограничение по запятым в аргументе макросу.

Использование VARIADIC макросов, макрос вместо будет выглядеть следующим образом:

#define TRACE(...)      \ 
    do         \ 
    {         \ 
     if (logger.Trace.Enabled())  \ 
     {        \ 
      logger.Trace << __VA_ARGS__; \ 
     }        \ 
    } while (false) 
+0

Вы можете решить эту проблему без вариативных макросов. Определите 'TRACE' как макрос, который не принимает никаких аргументов, где« TRACE »заменяется вызовом функции, которая возвращает указатель на функцию, которая принимает переменные аргументы. Этот вызов функции возвращает одну из двух функций в зависимости от включенного ведения журнала. Если ведение журнала включено, верните функцию, которая фактически регистрирует данные. Если ведение журнала не включено, верните функцию, которая просто возвращается без каких-либо действий: 'void (* traceFunc) (const char * fmt, ...);' then '#define TRACE traceFunc()'. Не делает C++ IO, хотя –

+2

@AndrewHenle, но затем, даже если ведение журнала отключено, сообщение журнала все еще вычисляется и затем отбрасывается, чего OP хочет избежать. –

+0

@ el.pescado Правда. Я не думаю, что можно полностью решить этот аспект проблемы, не используя переменные макросы. Конечно, было бы интересно увидеть такое решение. Было бы интересно также чистое решение C++ IO - под этим я подразумеваю тот, который не требует скобки C-стиля вообще - он просто использует оператор '<<'. –

0

Я работал над этой проблемой. Finnaly, я создал класс Log с этим интерфейсом и связанных с ними макросов:

class Log 
{ 
    public: 
    static bool trace_is_active(); 
    static bool debug_is_active(); 
    static bool info_is_active(); 
    static bool warning_is_active(); 
    static bool error_is_active(); 
    static void write_as_trace(const std::string& msg); 
    static void write_as_debug(const std::string& msg); 
    static void write_as_info(const std::string& msg); 
    static void write_as_warning(const std::string& msg); 
    static void write_as_error(const std::string& msg); 
}; 

#define LOG_TRACE(X) {if(Log::trace_is_active()){std::ostringstream o__;o__<<X;Log::write_as_trace(o__.str());}} 
#define LOG_DEBUG(X) {if(Log::debug_is_active()){std::ostringstream o__;o__<<X;Log::write_as_debug(o__.str());}} 
#define LOG_INFO(X) {if(Log::info_is_active()){std::ostringstream o__;o__<<X;Log::write_as_info(o__.str());}} 
#define LOG_WARNING(X) {if(Log::warning_is_active()){std::ostringstream o__;o__<<X;Log::write_as_warning(o__.str());}} 
#define LOG_ERROR(X) {if(Log::error_is_active()){std::ostringstream o__;o__<<X;Log::write_as_error(o__.str());}} 

Тогда использование очень просто и понятно:

//... 
LOG_WARNING("The variable x = " << x << " is out of range"); 
//... 
LOG_DEBUG("The inverse of the matrix is inv(m) = " << std::endl << inv(m) << std::endl); 

EDIT: Хочу отметить, что решение с do{...}while(false) лучше, потому что мое решение, за которым следует ;, является двумя инструкциями и не может использоваться в циклах или состоянии без записи между { и }. Теперь я могу улучшить свой код. :-)

Смежные вопросы