2015-05-26 4 views
-1

Я хотел бы создать класс для ведения журнала, который будет вести себя как std::cout, но автоматически добавит дополнительную информацию в поток.перегрузка << оператор и параметры передачи для std :: cout

обычая образец, который я хочу, было бы что-то вроде (позволяет не заботиться об объекте и типе контекста на данный момент, только предположить, что они являются станд :: строка):

Logger l; 

l << "Event with object : " << obj << " while in context : " << context; 

Тогда результат будет:

[timestamp] Event with object : [obj_desc] while in context : [this context][eol][flush] 

Я пытался с:

template<typename T> 
Logger& operator << (const T& msg){ 
    std::cout << timestamp() << msg << std::endl << std::flush; 
    return *this; 
} 

но мне кажется, что std::cout не может разрешить typename T и не выводит строку std :: например, во время segfault.

Возможным решением было бы перегрузить это для всех типов, но это довольно раздражает и требует много времени.

Есть ли лучший вариант для украшения std::cout продукции с дополнительной информацией?

Edit:

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

+2

'std :: endl' уже очищает поток. – chris

+1

см.: [Обработка перегрузки std :: endl?] (Http://stackoverflow.com/q/2212776/14065) –

+0

Пожалуйста, предоставьте полный (снятый, но полный) код для воспроизведения. Тогда не перефразируйте ошибку, но цитируйте это тоже. Скорее всего, вы используете это на временном или постоянном 'Logger' или пытаетесь передать в него перегруженную функцию, такую ​​как' std :: endl', но без фактического кода и ошибки это невозможно. –

ответ

0

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

Это заявление:

Logger l; 
l << "Event with object : " << obj << " while in context : " << context; 

в основном делает это (предполагая, что operator<< является членом Logger, что ваша реализация подразумевает это):

Logger l; 
l.operator<<("Event with object : ").operator<<(obj).operaator<<(" while in context : ").operator<<(context); 

Итак, вам нужно отдельно перегруженные operator<< для строки, obj, контекста и т. д. И вам нужен способ указать, когда нужно очистить полное сообщение журнала до std::cout.

Я хотел бы предложить что-то больше, как это:

struct LoggerStream 
{ 
    std::ostringstream strm; 

    struct Timestamp 
    { 
    }; 

    ~LoggerStream() 
    { 
     std::string s = strm.str(); 
     if (!s.empty()) 
      std::cout << s << std::flush; 
    } 

    LoggerStream& operator<< (const Timestamp &t) 
    { 
     strm << "[timestamp] "; // format this however you need 
     return *this; 
    } 

    LoggerStream& operator<< (const object &obj) 
    { 
     strm << "[obj_desc]"; // format this however you need 
     return *this; 
    } 

    LoggerStream& operator<< (const context &ctx) 
    { 
     strm << "[this context]"; // format this however you need 
     return *this; 
    } 

    LoggerStream& operator<< (std::ostream&(*f)(std::ostream&)) 
    { 
     if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush) 
     { 
      std::string s = strm.str(); 
      if (!s.empty()) 
       std::cout << s << std::flush; 
      strm.str(""); 
      strm.clear(); 
     } 
     else 
      strm << f; 

     return *this; 
    } 

    template<typename T> 
    LoggerStream& operator<< (const T& value) 
    { 
     strm << value; 
     return *this; 
    } 
}; 

class Logger 
{ 
    LoggerStream getStream() 
    { 
     LoggerStream strm; 
     strm << Timestamp; 
     return strm; 
    } 
}; 

Затем вы можете сделать что-то вроде этого:

Logger l; 
l.getStream() << "Event with object : " << obj << " while in context : " << context; 
... 
l.getStream() << "Event with object : " << obj << " while in context : " << context; 
... 

Logger l; 
LoggerStream strm = l.getStream(); 
strm << "Event with object : " << obj << " while in context : " << context << std::flush; 
... 
strm << Logger::Timestamp << "Event with object : " << obj << " while in context : " << context << std::flush; 
... 

В качестве альтернативы:

struct Logger 
{ 
    std::ostringstream strm; 

    ~Logger() 
    { 
     std::string s = strm.str(); 
     if (!s.empty()) 
      std::cout << "[timestamp] " << s << std::flush; 
    } 

    Logger& operator<< (const object &obj) 
    { 
     strm << "[obj_desc]"; // format this however you need 
     return *this; 
    } 

    Logger& operator<< (const context &ctx) 
    { 
     strm << "[this context]"; // format this however you need 
     return *this; 
    } 

    Logger& operator<< (std::ostream&(*f)(std::ostream&)) 
    { 
     if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush) 
     { 
      std::string s = strm.str(); 
      if (!s.empty()) 
       std::cout << "[timestamp] " << s << std::flush; 
      strm.str(""); 
      strm.clear(); 
     } 
     else 
      strm << f; 

     return *this; 
    } 

    template<typename T> 
    Logger& operator<< (const T& value) 
    { 
     strm << value; 
     return *this; 
    } 
}; 

Logger() << "Event with object : " << obj << " while in context : " << context; 
... 
Logger() << "Event with object : " << obj << " while in context : " << context; 
... 

Logger l; 
l << "Event with object : " << obj << " while in context : " << context << std::flush; 
... 
l << "Event with object : " << obj << " while in context : " << context << std::flush; 
... 
0

Можно, конечно, перегружать классы потоков, если вы хотите, обеспечивая operator<< для всех типов данных, которые вы хотите поддерживать (и это, вероятно, «правильный» путь), но, если все вы после быстрый способ добавить протоколирование в обычный поток, есть более простой способ:

#include <iostream> 
#include <iomanip> 
#include <sstream> 
#include <ctime> 
#include <unistd.h> 

#define logcout std::cout << timestamp() 

std::string timestamp(void) { 
    time_t now = time(0); 
    struct tm *tmx = localtime(&now); 
    std::ostringstream oss; 
    oss << '[' 
     << (tmx->tm_year+1900) 
     << '-' 
     << std::setfill('0') << std::setw(2) << (tmx->tm_mon+1) 
     << '-' 
     << std::setfill('0') << std::setw(2) << (tmx->tm_mday) 
     << ' ' 
     << std::setfill('0') << std::setw(2) << (tmx->tm_hour) 
     << ':' 
     << std::setfill('0') << std::setw(2) << (tmx->tm_min) 
     << ':' 
     << std::setfill('0') << std::setw(2) << (tmx->tm_sec) 
     << "] "; 
    return oss.str(); 
} 

int main (int argc, char *argv[]) { 
    logcout << "A slightly\n"; 
    sleep (5); 
    logcout << "sneaky" << " solution\n"; 
    return 0; 
} 

который Выходы:

[2015-05-26 13:37:04] A slightly 
[2015-05-26 13:37:09] sneaky solution 

Не обманывайте себя по размеру коды, я просто предоставил полный компилируемый образец для тестирования. Суть вопроса является одного линии:

#define logcout std::cout << timestamp() 

, где вы можете использовать logcout вместо std::cout, и каждого вхождения префиксов содержимого потока с произвольной строкой (метками времени в этом случае, на долю которого приходится основная часть кода).

Это не то, что я бы назвал самым чистым кодом C++, но если ваши потребности в основном вы заявили, это, безусловно, сделает трюк.

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