std :: stringstream作为参数

时间:2011-06-08 14:15:13

标签: c++ logging parameters stringstream

我对C ++语言有些陌生。我正在编写一个用于记录到文件的实用程序类。它工作得很漂亮,除了现在我想通过使它更方便使用(例如将字符串流传递给日志函数)来增强它。

这是我一直在尝试的,但它没有奏效。

定义:

void LogStream( std::stringstream i_Log ){ m_FileHandle << i_Log << std::endl; }

呼叫:

m_LogObject->LogStream( "MKLBSearchEngine::Search( " << x << ", " << i_Filter << " ) - No Results Found" );

6 个答案:

答案 0 :(得分:11)

您的解决方案存在一些问题。首先是你 按值传递stringstream,它不支持复制。你需要 引用。第二个是在呼叫站点,返回值 operator<<重载是ostream&,而不是stringstream,从那以后 stringstream不是ostream&的基类(这是另一种方式 圆),你不能初始化stringstream(或stringstream&) 用它。最后,没有operator<<需要一个 stringstream作为右手参数,所以声明在 LogStream功能无法正常工作。最后,这将是一些 无论如何,对用户来说很尴尬。 operator<<的日志是非成员, 使用ostream&非const引用作为第一个参数,所以你不能 用左边的参数作为临时调用它们。 (在你的示例电话中, 当然,你忘了创建std::ostringstream了;它 不会编译,因为没有<<的重载,其左侧操作数为char const[]char const*。)

几乎所有这些问题都有解决方法。某物 像:

void LogStream( std::ostream& text )
{
    std::ostringstream& s = dynamic_cast<std::ostringstream&>(text);
    m_FileHandle << s.str() << std::endl;
}

处理除了最后一个之外的所有问题;最后一个必须处理 由客户,如:

m_LogObject->LogStream( std::ostringstream().flush() << "..." << x );

(对std::ostream::flush()的调用返回非const引用 流,可用于进一步初始化std::ostream&。 虽然你不能用临时的初始化非const引用, 你可以在上面调用非const成员函数。)

客户端代码的这种尴尬使我通常更喜欢 更复杂的解决方案我定义了一个特殊的LogStreamer类, 类似的东西:

class LogStreamer
{
    boost::shared_ptr< std::ostream > m_collector;
    std::ostream* m_dest;

public:
    LogStreamer( std::ostream& dest )
        , m_collector( new std::ostringstream )
        , m_dest( &dest )
    {
    }
    ~LogStreamer()
    {
        if ( m_collector.unique() ) {
            *m_dest << m_collector->str() << std::endl;
        }
    }
    template <typename T>
    LogStreamer& operator<<( T const& value )
    {
        *m_collector << value;
        return *this;
    }
};

LogStreamer LogStream() { return LogStreamer( m_FileHandle ); }

然后客户端代码可以写:

m_LogObject->LogStream() << "..." << x;

在我自己的代码中:日志对象始终是单例,调用是 通过宏将__FILE____LINE__传递给LogStream() 函数,最终目标ostream是一个特殊的streambuf 由LogStream()调用的特殊函数,它接受一个文件名和一个 行号,在时间戳的开头输出它们和时间戳 下一行输出,并缩进所有其他行。过滤 streambuf有类似的东西:

class LogFilter : public std::streambuf
{
    std::streambuf* m_finalDest;
    std::string m_currentHeader;
    bool m_isAtStartOfLine;
protected:
    virtual int overflow( int ch )
    {
        if ( m_isAtStartOfLine ) {
            m_finalDest->sputn( m_currentHeader.data(), m_currentHeader.size() );
            m_currentHeader = "    ";
        }
        m_isAtStartOfLine = (ch == '\n');
        return m_finalDest->sputc( ch );
    }
    virtual int sync()
    {
        return m_finalDest->sync();
    }

public:
    LogFilter( std::streambuf* dest )
        : m_finalDest( dest )
        , m_currentHeader( "" )
        , m_isAtStartOfLine( true )
    {
    }
    void startEntry( char const* filename, int lineNumber )
    {
        std::ostringstream header;
        header << now() << ": " << filename << " (" << lineNumber << "): ";
        m_currentHeader = header.str();
    }
};

(函数now()当然会返回一个std::string 时间戳。或struct tm,您为<<撰写tm。)

答案 1 :(得分:1)

您的设计存在问题。您不希望接受流作为参数,接受字符串,或使您的类表现为流(或两者)。

如果使对象表现为流,则执行以下操作:

m_LogObject << "what to log" << etc;

为此,只需覆盖<<运算符。

答案 2 :(得分:1)

您的通话应该是

m_LogObject->LogStream( stringstream() << "MKLBSearchEngine::Search( " << x
 << ", " << i_Filter << " ) - No Results Found" );

因为你需要创建你将传递给函数的stringstream对象。

此调用意味着您已经拥有了所需的输出流,因此我还建议您更改类设计以使用operator<<进行日志记录,除非它已经过载。

答案 3 :(得分:1)

您的函数调用将不起作用,因为"MKLBSearchEngine::Search( "的类型为const char *且<<运算符没有重载。它也不适用于std::string("MKLBSearchEngine::Search( "),因为std::string也没有这样的运算符。您可以做的是使用std::stringstream("MKLBSearchEngine::Search( ")调用它,它将第一个参数转换为流,以便以下运算符在此流上工作。但正如其他人所指出的那样,你必须使函数参数成为一个const引用,因为流是不可复制的(即使那样效率也很低)。另外,只是在文件中写入std::stringstream将无法执行您想要的操作(如果它仍然有效),而是必须获取其内容(基础std::string)。所以你的所有代码都应该是这样的:

void LogStream( const std::stringstream &i_Log ){ m_FileHandle << i_Log.str() << std::endl; }
...
m_LogObject->LogStream( std::stringstream("MKLBSearchEngine::Search( ") << x << ", " << i_Filter << " ) - No Results Found" );

但你也可以使用LogString(const std::string &)并让这个函数的用户自己调用stream.str()

答案 4 :(得分:0)

您不能按值传递流对象(因为它们不可复制),因此您需要传递(并存储)引用:

void LogStream(std::stringstream& i_Log){
    m_FileHandle << i_Log << std::endl;
}

这可能不会做你期望的事情(它可能会打印一个i_Log的地址,原因相当模糊)。

如果您的意图是从字符串流中取出东西,这可能会做你想要的:

i_Log.get( *m_FileHandle.rdbuf() );

答案 5 :(得分:0)

您正在按值传递std::stringstream个实例。您希望避免复制并通过引用(或指针)传递它。例如:

void LogStream ( std::stringstream & i_Log ){ m_FileHandle << i_Log << std::endl; }

详细了解C ++ references