Как использовать мой класс журналирования как стандартный поток C ++? - PullRequest
13 голосов
/ 04 февраля 2009

У меня есть рабочий класс логгера, который выводит некоторый текст в richtextbox (Win32, C ++). Проблема в том, что я всегда использую это так:

stringstream ss;  
ss << someInt << someString;  
debugLogger.log(ss.str());

Вместо этого было бы гораздо удобнее использовать его как поток, как в:

debugLogger << someInt << someString;

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

Ответы [ 5 ]

32 голосов
/ 04 февраля 2009

Вам необходимо реализовать operator << соответственно для вашего класса. Общая схема выглядит следующим образом:

template <typename T>
logger& operator <<(logger& log, T const& value) {
    log.your_stringstream << value;
    return log;
}

Обратите внимание, что это относится к (не const) ссылкам, так как операция изменяет ваш регистратор. Также обратите внимание, что вам нужно вернуть параметр log, чтобы цепочка работала:

log << 1 << 2 << endl;
// is the same as:
((log << 1) << 2) << endl;

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

14 голосов
/ 04 февраля 2009

Перегрузка оператора вставки << не подходит. Вам нужно будет добавить перегрузки для всех endl или любых других пользовательских функций. </p>

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

Вот несколько простых примеров:

1 голос
/ 08 марта 2018

Элегантным решением, которое также решает проблемы с промывкой, является следующее:

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

class Logger
{
    using Stream = std::ostringstream;
    using Buffer_p = std::unique_ptr<Stream, std::function<void(Stream*)>>;

public:
    void log(const std::string& cmd) {
        std::cout << "INFO: " << cmd << std::endl;
    }

    Buffer_p log() {
        return Buffer_p(new Stream, [&](Stream* st) {
            log(st->str());
        });
    }
};

#define LOG(instance) *(instance.log())

int main()
{
    Logger logger;
    LOG(logger) << "e.g. Log a number: " << 3;
    return 0;
}
1 голос
/ 27 января 2018

Как отметил Люк Эрмитт , есть "Вход в C ++" статья, которая описывает очень аккуратный подход к решению этой проблемы. В двух словах, если у вас есть такая функция:

void LogFunction(const std::string& str) {
    // write to socket, file, console, e.t.c
    std::cout << str << std::endl;
}

можно написать обертку для использования в std :: cout следующим образом:

#include <sstream>
#include <functional>

#define LOG(loggingFuntion) \
    Log(loggingFuntion).GetStream()

class Log {
    using LogFunctionType = std::function<void(const std::string&)>;

public:
    explicit Log(LogFunctionType logFunction) : m_logFunction(std::move(logFunction)) { }
    std::ostringstream& GetStream() { return m_stringStream; }
    ~Log() { m_logFunction(m_stringStream.str()); }

private:
    std::ostringstream m_stringStream;
    LogFunctionType m_logFunction;
};

int main() {
    LOG(LogFunction) << "some string " << 5 << " smth";
}

( онлайн-демонстрация )

Также, есть очень хорошее решение , предоставленное Стюартом.

0 голосов
/ 04 февраля 2009

В классе Logger переопределите оператор <<. </p>

Нажмите Здесь , чтобы узнать, как реализовать оператор <<. </p>

Вы также можете избежать записи операторов внутри кода использование аспектно-ориентированного программирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...