Как 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;

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

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

Ответы

Ответ 1

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

Ответ 2

Что-то вроде этого будет делать:

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";

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

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

Ответ 3

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

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";

Ответ 4

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

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

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