2011-12-22 4 views
2

Я пытаюсь сделать простую библиотеку регистрации только для себя. Я знаю, что существует несколько раз, но я не нашел ни одной, только маленькой и очень «C++», такой как библиотека регистрации, которая вписывается в мое приложение.Остановить оператора переопределения <<

В настоящее время у меня есть следующий синтаксис:

logger << debug << "A debug message" << end; //debug and end is my custom manipulators 

Я выполнил все необходимые оператору < < и он прекрасно работает, особенно когда она имеет обратную совместимость с станд :: ostream. Но мне интересно, просто для эффективности, если это почему прекратить оценивать что-либо, если какое-то сообщение не должно регистрироваться (после отладки в примере)? Сделать все после того, как манипулятор серьезности «исчезнет»?

Только сейчас у меня есть следующий код короче:

template <typename Type> 
Logger & Logger::operator<<(const Type & message) 
{ 
    if(this->get_message_level() <= this->get_severity()) 
    { 
     BOOST_FOREACH(std::ostream* stream, this->_sinks) 
     { 
      *stream << message; 
     } 
    } 
    return *this; 
} 

Logger & Logger::operator<< (Logger & (*pf)(Logger&)) 
{ 
    return pf(*this); 
} 

Logger & debug(Logger& logger) 
{ 
    logger.lock(); 
    logger.set_severity(7); 
    //... 
    return logger; 
} 

Logger & end(Logger& logger) 
{ 
    logger << std::endl; 
    logger.unlock(); 
    return logger; 
} 

Спасибо заранее.

ответ

1

Это может быть немного сложнее, в зависимости от того, что compromizes вы готовы переменного тока cept в синтаксисе. Если вы действительно хотите, чтобы поддержать все, что выводящий к ostream делает, то лучшее, что можно сделать (насколько я знаю) является классом-оболочкой, вдоль линий:

class Logger 
{ 
    std::ostream* myDest; 
public: 
    // Appropriate constructors... 
    template<typename T> 
    Logger& operator<<(T const& obj) 
    { 
     if (myDest != NULL) 
      (*myDest) << obj; 
     return *this; 
    } 
}; 

Если вход включен off (Logger::myDest == NULL), ни один из кода преобразования не будет выполнен, но вы все равно оцените каждый из аргументов . По моему опыту, это обычно не проблема, так как большинство аргументов являются либо строковыми литералами, либо простой переменной, но это не общая стоимость 0. Он также имеет потенциальный недостаток: распространенный тип неstd::ostream& (хотя я так и не нашел , это на практике проблема).

Несколько Tricker решением было бы использовать макрос вдоль линий:

#define logger loggerInstance != NULL && (*loggerInstance) 

Это еще позволит большинство реальных применений с тем же синтаксисом (потому что оператор && имеет очень низкий приоритет) , но может завершиться ошибкой в ​​случаях, когда кто-то попытался быть слишком умным, и ввел вывод журнала в более сложное выражение. В дополнение к тому, чтобы не делать преобразования , аргументы даже не оцениваются, если ведение журнала отключено .

Наконец, если вы принимаете другой синтаксис, вы можете написать что-то вроде:

#ifndef NDEBUG 
#define LOG(argList) logger << argList 
#else 
#define LOG(argList) 
#endif 

Это требует от пользователя, чтобы написать LOG("x = " << x), вместо log << "x = " << x, и требует перекомпиляции, если вы хотите, чтобы включить входа в систему, но это единственное решение, которое я знаю, у которого есть абсолютная стоимость 0, если каротаж отключен.

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

Обратите внимание, что даже с первым, вы, вероятно, хотите использовать макрос получить экземпляр регистратора, для того, чтобы автоматически вставлять __FILE__ и __LINE__, а во-вторых, вы, вероятно, все еще хотите используйте класс оболочки , чтобы обеспечить слив в деструкторе; если приложение многопоточно, вам понадобится обертка во всех случаях в , чтобы сделать всю последовательность выходных данных атомной.

4

Вы могли бы сделать что-то же просто, как

extern "C" bool want_log; 
#define LOG(Arg) do { if (want_log) \ 
    cout << __FILE__ << ":" << __LINE__ ": " << Arg << endl; } while(0) 

и использовать его в качестве LOG("x=" << x)

+1

Я предполагаю, что существует несколько разных степеней, и что «отладка» является лишь одним из них. Таким образом, вы можете захотеть чего-то более похожего на «LOG (logger, debug,« x = »<< x)» и '#define LOG (LOGGER, SEVERITY, ARG) do {if (want_log _ ## THEVERITY) {(LOGGER) << (БЕЗОПАСНОСТЬ) << ARG << конец; } while (0) '. –

+0

@SteveJessop Если существует несколько ограничений, и вы хотите иметь 0 стоимости дезактивированных, вам понадобятся несколько макросов: 'LOG_TRACE',' LOG_WARNING' и т. Д. И для чего-либо, кроме 0 стоимости, есть лучшие решения , –

+0

Просто интересно, но почему 'extern 'C" 'на булевом? (Я особенно задумываюсь о его использовании на 'bool', потому что C и C++ определяют« bool »в радикально разных манерах.) –

0

Вы можете проверить тяжесть в операторе Logger & Logger::operator<< (Logger & (*pf)(Logger&)) и просто возвращает «пустой» регистратор, который не печатает ничего в этом случае:

class EmptyLogger : public Logger { 
    template <typename Type> 
    Logger & Logger::operator<<(const Type & message) { return *this; } 
}; 

EmptyLogger empty; // Can be global/static, it won't cause any race conditions as it does nothing 

Logger & Logger::operator<< (Logger & (*pf)(Logger&)) 
{ 
    if (logger.get_severity() < 5) // Replace with a proper condition 
     return empty; 
    return pf(*this); // What does pf() do? Aren't you returning reference to a temporary object? 
} 
+0

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

+0

Это правильно. Без макросов я сомневаюсь, что есть способ остановить оценку аргументов. Тем не менее, я думаю, что интеллектуальный компилятор фактически удалит эти вызовы (потому что они просто «возвращают * это»), когда оптимизация включена. –

+0

@dark_charlie: только если значение «правильного условия» известно во время компиляции. Мне непонятно, определяется ли уровень протоколирования во время компиляции или аргументы командной строки или еще что-то. И, конечно, оценка аргументов не должна иметь побочных эффектов для ее исключения в качестве оптимизации, тогда как ответ Базиля также пропускает побочные эффекты, когда ведение журнала не требуется. –

0

Взгляните на эту статью в др Доббс о входе в C++:

http://drdobbs.com/cpp/201804215?pgno=2

Эта страница обращается ваше особое беспокойство, но я рекомендую прочитать всю статью.

Я внедрил систему регистрации на основе этой статьи и был очень доволен ею.

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