Custom stream to method in C++?

后端 未结 5 512
星月不相逢
星月不相逢 2020-12-06 08:37

I\'m making a logger and I wish to have some kind of stream-like happenings going on, ideally doing CLogger << \"Testing, \" << 1 << \",2,3\\n\";

相关标签:
5条回答
  • 2020-12-06 09:02

    Implement a proxy object that gives you operator<< and pass an ownership marker to the returned proxy object. When an object with the ownership marker dies, you flush the stream.

    An easy way to do this would be to wrap ostringstream in an auto_ptr in your proxy and flushing to your logger when the auto_ptr is not null in the proxy's d-tor.

    That'll give you the formatting possible with ostream, but still result in only one call to your logger, which I thought was the real problem.

    Think of something like this:

    class CLoggingProxy
    {
    public:
      template <class T>
      CLoggingProxy operator<<( const T& rhs )
      {
        if ( stream )
          *stream << rhs;
        return *this;
      }
    
      ~CLoggingProxy()
      {
        if ( stream )
          logger->log(stream->str());
      }
    
    private:
      std::auto_ptr<std::ostringstream> stream; 
      CLogger* logger;
    
      friend class CLogger;
      CLoggingProxy( CLogger* logger ) // call this e.g. from the logger to "start" input
      : stream(new std::ostringstream), logger(logger) {}
    };
    
    0 讨论(0)
  • 2020-12-06 09:07

    Check out operator <<, which is what STL's streams overload.

    class CLogger
    {
    public:
        CLogger& operator << (const std::string& _rhs)
        {
            // work with it here
            return *this;
        }; // eo operator <<
    }; // eo class CLogger
    

    EDIT:

    See this page that outlines how std::ostream overloads operator << for different types:

    http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C/

    0 讨论(0)
  • 2020-12-06 09:08

    I'm just going to copy-paste my current implementation of this below, it does all you need (and handles things like std::endl and the like). AMBROSIA_DEBUGis macro defined in debug builds, so in theory, every call to this output class should be omitted in release builds (haven't checked though, but seems logical overhead is kept to a minimum. The functionality is based on QDebug functionality, plus a little addition of mine debugLevel, which would allow you to filter debug messages by hand in your code depending on a runtime parameter. Right now it also adds the same amount of spaces before each message.

    // C++ includes
    #include <iostream>
    #include <string>
    
    typedef std::ostream& (*STRFUNC)(std::ostream&);
    
    #ifdef AMBROSIA_DEBUG
        static int debugLevel;
        const static int maxDebugLevel = 9;
    #endif
    
    class Debug
    {
    public:
    
        #ifdef AMBROSIA_DEBUG
        Debug( const int level = 0 )
        : m_output( level <= debugLevel ),
          m_outputSpaces( true ),
          m_spaces( std::string(level, ' ') )
        #else
        Debug( const int )
        #endif // AMBROSIA_DEBUG
        {}
    
        template<typename T>
        #ifdef AMBROSIA_DEBUG
        Debug& operator<<( const T &output )
        {
            if( m_output )
            {
                if( m_outputSpaces )
                {
                    m_outputSpaces = false;
                    std::cerr << m_spaces;
                }
                std::cerr << output;
            }
        #else
        Debug& operator<<( const T & )
        {
        #endif // AMBROSIA_DEBUG
            return *this;
        }
        // for std::endl and other manipulators
        typedef std::ostream& (*STRFUNC)(std::ostream&);
        #ifdef AMBROSIA_DEBUG
        Debug& operator<<( STRFUNC func )
        {
            if( m_output )
                func(std::cerr);
        #else
        Debug& operator<<( STRFUNC )
        {
        #endif // AMBROSIA_DEBUG
            return *this;
        }
    private:
    #ifdef AMBROSIA_DEBUG
        bool m_output;
        bool m_outputSpaces;
        std::string m_spaces;
    #endif // AMBROSIA_DEBUG
    };
    

    Example usage:

    int main()
    {
        debugLevel = 9; // highest allowed in my app...
        Debug(4) << "This message should have an indentation of 4 spaces." << endl;
        Debug(8) << "This is a level 8 debug message.\n";
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-06 09:12

    All of the operator<<() functions are defined on the class ostream, which you can inherit from and implement its methods.

    0 讨论(0)
  • 2020-12-06 09:15

    If all that you need is directing certain log messages to files, have you considered std::ofstream?

    Otherwise, I like to derive my logging class from std::ostream, so I get all of the stream goodness. The trick is to put all of your application-specific code in the associated streambuf class. Consider:

    #include <iostream>
    #include <sstream>
    
    class CLogger : public std::ostream {
    private:
        class CLogBuf : public std::stringbuf {
        private:
            // or whatever you need for your application
            std::string m_marker;
        public:
            CLogBuf(const std::string& marker) : m_marker(marker) { }
            ~CLogBuf() {  pubsync(); }
            int sync() {
                std::cout << m_marker << ": " << str();
                str("");
                return std::cout?0:-1;
            }
    
        };
    
    public:
        // Other constructors could specify filename, etc
        // just remember to pass whatever you need to CLogBuf
        CLogger(const std::string& marker) : std::ostream(new CLogBuf(marker)) {}
        ~CLogger() { delete rdbuf(); }
    };
    
    int main()
    {
        CLogger hi("hello");
        CLogger bye("goodbye");
    
        hi << "hello, world" << std::endl;
        hi << "Oops, forgot to flush.\n";
        bye << "goodbye, cruel world\n" << std::flush;
        bye << "Cough, cough.\n";
    }
    

    Notes:

    • The CLogger constructor can take whatever parameters you need to use -- a filename, an output language, a pointer to the underlying log data, whatever. Just pass the data onto the CLogBuf class.
    • The CLogBuf's sync() is automatically called during in response to std::flush.
    0 讨论(0)
提交回复
热议问题