2016-10-26 1 views
2

Я хотел бы написать удобный интерфейс для моей очень простой библиотеки журналов. Возьмите два следующих фрагмента кода. Первый из них это то, что я делаю сейчас, вторая моя идея интуитивного интерфейса:Как написать iostream-подобный интерфейс для регистрации библиотеки?

std::ostringstream stream; 
stream<<"Some text "<<and_variables<<" formated using standard string stream" 
logger.log(stream.str()); //then passed to the logger 

И

logger.convinient_log()<<"Same text "<<with_variables<<" but passed directly"; 

Мой мыслительный процесс-дизайн за что идея заключается в том, чтобы вернуть какой-то временный stringstream-like объект от logger.convinient_log() функция. Этот объект уничтожения (я надеюсь, что это произойдет в конце строки или в подобном, удобном месте) будет собирать строку из себя и называть фактическим logger.log(). Дело в том, что я хочу обрабатывать его целиком, а не по отдельности, так что log() может добавить, например. префикс и sufix для всей строки текста.

Я очень хорошо понимаю, что это может быть прямо невозможно или невозможно без какой-либо тяжелой магии. Если это так, то какой был бы почти-удобный способ сделать это и как его реализовать? Я ставлю на передачу некоторой специальной переменной, которая заставила бы операцию call-call-logger.log().

Если вы не знаете точного ответа, ресурсы по этой теме (например, расширение stringstream) также будут приветствоваться.

+1

Возможный дубликат (http://stackoverflow.com/questions/511768/ как-to-use-my-logging-class-like-a-std-c-stream) – user4581301

+0

Почти дубликат, я хочу что-то другое: я хочу обрабатывать весь ввод одного 'convient_log()' сразу, а не кусок ие части. Если вы говорите «удачи», есть ли у вас какие-либо улучшения (с точки зрения реализации и аналогичного удобства интерфейса)? Я был бы очень благодарен за это :) – psorek

ответ

5

Так работает Boost.Log. Основная идея проста:

struct log 
{ 
    log() { 
     uncaught = std::uncaught_exceptions(); 
    } 

    ~log() { 
     if (uncaught >= std::uncaught_exceptions()) { 
      std::cout << "prefix: " << stream.str() << " suffix\n"; 
     } 
    } 

    std::stringstream stream; 
    int uncaught; 
}; 

template <typename T> 
log& operator<<(log& record, T&& t) { 
    record.stream << std::forward<T>(t); 
    return record; 
} 

template <typename T> 
log& operator<<(log&& record, T&& t) { 
    return record << std::forward<T>(t); 
} 

// Usage: 
log() << "Hello world! " << 42; 

std::uncaught_exceptions() используется, чтобы избежать регистрации неполное сообщение, если исключение в середине.

+0

И для случая OP они могут просто хранить ссылку на свой экземпляр журнала в конструкторе, поэтому он будет выглядеть как 'удобный_log (logger) <<" Тот же текст "<< с_вариабельс <<" но переданы напрямую ";' –

+0

Это именно то, что мне нужно! Еще один вопрос: какова область действия 'log()' eg. когда он будет уничтожен? – psorek

+0

@psorek Temporaries живут до конца выражения, которое их создало, т. Е. Точки с запятой. См. [Время жизни временного объекта] (http://en.cppreference.com/w/cpp/language/lifetime#Temporary_object_lifetime). –

1

Создать собственный класс, производный от std::basic_streambuf записать на свой регистратор, например:

class LoggerBuf : public std::stringbuf 
{ 
private: 
    Logger logger; 
public: 
    LoggerBuf(params) : std::stringbuf(), logger(params) { 
     ... 
    } 

    virtual int sync() { 
     int ret = std::stringbuf::sync(); 
     logger.log(str()); 
     return ret; 
    } 
}; 

И тогда вы можете создать экземпляр std::basic_ostream объект, придав ему указатель на LoggerBuf объекта, например:

LoggerBuf buff(params); 
std::ostream stream(&buf); 
stream << "Some text " << and_variables << " formated using standard string stream"; 
stream << std::flush; // only if you need to log before the destructor is called 

В качестве альтернативы выведите собственный класс от std::basic_ostream, чтобы обернуть свой класс LoggerBuf, например:

class logger_ostream : public std::ostream 
{ 
private: 
    LoggerBuf buff; 
public: 
    logger_ostream(params) : std:ostream(), buff(params) 
    { 
     init(&buff); 
    } 
}; 

std::logger_ostream logger(params); 
logger << "Some text " << and_variables << " formated using standard string stream"; 
logger << std::flush; // only if you need to log before the destructor is called 
1

Вот класс, который я давно написал. Это похоже на то, что вы ищете. Я смог добиться этого без какого-либо напуганного наследования ostream, stream_buf или чего-либо еще. Вы можете записывать файлы, консоли, сокеты или все, что захотите, всякий раз, когда поймает флеш.

Он не работает с ostream_iterators, но хорошо выполняет все функции io_manip.

Использование: [? Как использовать мой класс протоколирования как поток станд C++]

Logger log; 

int age = 32; 
log << "Hello, I am " << age << " years old" << std::endl; 
log << "That's " << std::setbase(16) << age << " years in hex" << std::endl; 
log(Logger::ERROR) << "Now I'm logging an error" << std::endl; 
log << "However, after a flush/endl, the error will revert to INFO" << std::end; 

Реализация

#include <iostream> 
#include <sstream> 
#include <string> 

class Logger 
{ 
public: 
    typedef std::ostream& (*ManipFn)(std::ostream&); 
    typedef std::ios_base& (*FlagsFn)(std::ios_base&); 

    enum LogLevel 
    { 
     INFO, 
     WARN, 
     ERROR 
    }; 

    Logger() : m_logLevel(INFO) {} 

    template<class T> // int, double, strings, etc 
    Logger& operator<<(const T& output) 
    { 
     m_stream << output; 
     return *this; 
    } 

    Logger& operator<<(ManipFn manip) /// endl, flush, setw, setfill, etc. 
    { 
     manip(m_stream); 

     if (manip == static_cast<ManipFn>(std::flush) 
     || manip == static_cast<ManipFn>(std::endl)) 
      this->flush(); 

     return *this; 
    } 

    Logger& operator<<(FlagsFn manip) /// setiosflags, resetiosflags 
    { 
     manip(m_stream); 
     return *this; 
    } 

    Logger& operator()(LogLevel e) 
    { 
     m_logLevel = e; 
     return *this; 
    } 

    void flush() 
    { 
    /* 
     m_stream.str() has your full message here. 
     Good place to prepend time, log-level. 
     Send to console, file, socket, or whatever you like here. 
    */  

     m_logLevel = INFO; 

     m_stream.str(std::string()); 
     m_stream.clear(); 
    } 

private: 
    std::stringstream m_stream; 
    int    m_logLevel; 
}; 
Смежные вопросы