Как работает QDebug() << вещи; добавить новую строку автоматически?

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

struct debug
{
#if defined(DEBUG)
    template<typename T>
    std::ostream& operator<<(T const& a) const
    {
        std::cout << a;
        return std::cout;
    }
#else
    template<typename T>
    debug const& operator<<(T const&) const
    {
        return *this;
    }

    /* must handle manipulators (endl) separately:
     * manipulators are functions that take a stream& as argument and return a
     * stream&
     */
    debug const& operator<<(std::ostream& (*manip)(std::ostream&)) const
    {
        // do nothing with the manipulator
        return *this;
    }
#endif
};

Типичное использование:

debug() << "stuff" << "more stuff" << std::endl;

Но я бы не хотел добавлять std:: endl;

Мой вопрос в основном, как я могу узнать, когда возвращаемый тип оператора<< не будет использоваться другим оператором << (и, таким образом, добавить endl)?

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

4 ответа

Решение

Qt использует метод, похожий на @Evan. Подробности о реализации смотрите в версии qdebug.h, но они передают все в базовый текстовый поток, а затем очищают поток и конечную строку при уничтожении временного объекта QDebug, возвращаемого qDebug().

Что-то вроде этого подойдет:

struct debug {
    debug() {
    }

    ~debug() {
        std::cerr << m_SS.str() << std::endl;
    }

public:
    // accepts just about anything
    template<class T>
    debug &operator<<(const T &x) {
        m_SS << x;
        return *this;
    }
private:
    std::ostringstream m_SS;
};

Что должно позволить вам делать такие вещи:

debug() << "hello world";

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

ПРИМЕЧАНИЕ: непроверенный код, но должен работать:-)

Когда вы пишете, что это типичное использование:

debug() << "stuff" << "more stuff" << std::endl;

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

~debug()
{
    *this << std::endl;

    ... the rest of your destructor ...
}

Это означает, что вы не можете сделать что-то вроде этого:

// this won't output "line1" and "line2" on separate lines
debug d;
d << "line1";
d << "line2";

Вставка потока (<<) и извлечение (>>) должны быть не членами.

Мой вопрос в основном, как я могу узнать, когда возвращаемый тип оператора<< не будет использоваться другим оператором << (и, таким образом, добавить endl)?

Ты не можешь. Создайте функцию-член, чтобы специально добавить это или добавить endl как только эти цепные вызовы сделаны с. Хорошо документируйте свой класс, чтобы клиенты знали, как им пользоваться. Это ваша лучшая ставка.

Другие вопросы по тегам