← index
libmx2/MX2_Project/webmx/mx2/include/tee_stream.hpp
Source: libmx2/MX2_Project/webmx/mx2/include/tee_stream.hpp
/**
 * @file tee_stream.hpp
 * @brief Tee stream utilities that duplicate output to two sinks simultaneously.
 *
 * Provides TeeBuf and TeeStream, which mirror every byte written to both
 * underlying stream buffers.  The global system_out and system_err streams
 * are pre-wired to stdout/stderr and a log file after redirect() is called.
 */
#ifndef MX_TSTREAM_H

#define MX_TSTREAM_H


#include <fstream>

#include <iostream>

#include <memory>

#include <stdexcept>

#include <streambuf>


namespace mx {

    /**
     * @class TeeBuf
     * @brief A std::streambuf that writes every byte to two underlying buffers.
     *
     * Attach two std::streambuf pointers at construction.  Any character written
     * through this buffer is forwarded to both sinks; overflow() and sync() are
     * both propagated.
     */
    class TeeBuf : public std::streambuf {
      public:
        /**
         * @brief Construct a TeeBuf that mirrors output to buf1 and buf2.
         * @param buf1 First destination stream buffer (must outlive this object).
         * @param buf2 Second destination stream buffer (must outlive this object).
         */
        TeeBuf(std::streambuf *buf1, std::streambuf *buf2) : buf1(buf1), buf2(buf2) {}

      protected:
        /**
         * @brief Write a single character to both buffers.
         * @param c Character to write (may be EOF).
         * @return The character written on success, or traits_type::eof() on failure.
         */
        virtual int overflow(int c) override {
            if (buf1 == nullptr || buf2 == nullptr) {
                return traits_type::eof();
            }

            if (traits_type::eq_int_type(c, traits_type::eof())) {
                if (this->sync() == -1) {
                    return traits_type::eof();
                }
                return traits_type::not_eof(c);
            }

            const auto ch = traits_type::to_char_type(c);
            if (traits_type::eq_int_type(buf1->sputc(ch), traits_type::eof()) ||
                traits_type::eq_int_type(buf2->sputc(ch), traits_type::eof())) {
                return traits_type::eof();
            }

            return c;
        }

        /**
         * @brief Flush both underlying buffers.
         * @return 0 if both flushes succeed, -1 otherwise.
         */
        virtual int sync() override {
            if (buf1 == nullptr || buf2 == nullptr) {
                return -1;
            }
            int res1 = buf1->pubsync();
            int res2 = buf2->pubsync();
            return (res1 == 0 && res2 == 0) ? 0 : -1;
        }

      private:
        std::streambuf *buf1 = nullptr; ///< First sink.

        std::streambuf *buf2 = nullptr; ///< Second sink.

    };

    /**
     * @class TeeStream
     * @brief An std::ostream that writes to two output streams simultaneously.
     *
     * Wraps TeeBuf so all normal stream operators (<<, write, flush) are
     * mirrored to both stream1 and stream2.
     */
    class TeeStream : public std::ostream {
      public:
        /**
         * @brief Construct a TeeStream that writes to both stream1 and stream2.
         * @param stream1 First output stream.
         * @param stream2 Second output stream.
         */
        TeeStream(std::ostream &stream1, std::ostream &stream2)
            : std::ostream(&tbuf), tbuf(stream1.rdbuf(), stream2.rdbuf()) {}

      private:
        TeeBuf tbuf; ///< Internal tee buffer.

    };

    extern TeeStream system_out; ///< Global stdout + log tee stream.

    extern TeeStream system_err; ///< Global stderr + log tee stream.


    /**
     * @brief Redirect std::cout and std::cerr through the tee streams.
     *
     * After calling this function, all writes to std::cout and std::cerr
     * are forwarded to both the terminal and the configured log file.
     */
    void redirect();
} // namespace mx


#endif