std::stringstream 作为参数

发布于 2024-11-14 08:39:06 字数 387 浏览 5 评论 0原文

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

这就是我一直在尝试的方法,但没有成功。

定义:

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

调用:

m_LogObject->LogStream( "MKLBSearchEngine::Search( " << x << ", " << i_Filter << " ) - 未找到结果” );

I'm somewhat new to the C++ language. I'm writing a utility class for logging to file. It works beautifully except that now I would like to enhance it by making it more convenient to use (e.g. pass stringstreams to a log function).

This is what I've been trying and it hasn't worked.

definition:

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

call:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

甜柠檬 2024-11-21 08:39:06

您的解决方案存在几个问题。第一个是你是
按值传递stringstream,并且不支持复制。你需要
通过参考。第二个是在调用站点,返回值
operator<< 重载是 ostream&,而不是 stringstream,并且因为
stringstream 不是 ostream& 的基类(这是另一种方式
round),您无法初始化 stringstream (或 stringstream&
与它。最后,没有 operator<< 需要一个
stringstream 作为右侧参数,因此语句中
LogStream 功能无法工作。最后,这将有点
无论如何对于用户来说都很尴尬。日志operator<<都是非会员,
使用 ostream& 非常量引用作为第一个参数,所以你不能
使用临时参数作为左参数来调用它们。 (在您的示例调用中,
当然,无论如何,您忘记创建 std::ostringstream ;它
不会编译,因为没有重载 << ,它需要一个 char
const[]
char const* 作为其左侧操作数。)

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

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() 的调用返回一个非常量引用
流,可用于进一步初始化 std::ostream&
虽然您无法使用临时变量初始化非常量引用,
您可以在其上调用非常量成员函数。)

对于客户端代码来说,这种尴尬使我通常更喜欢
更复杂的解决方案。我定义了一个特殊的 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 编写了一个 <<。)

There are several problems with your solution. The first is that you're
passing stringstream by value, and it doesn't support copy. You need
by reference. The second is that at the call site, the return value of
the operator<< overloads is ostream&, not stringstream, and since
stringstream isn't a base class of ostream& (it's the other way
round), you can't initialize the stringstream (or the stringstream&)
with it. And finally, there's no operator<< which takes a
stringstream as the right hand parameter, so the statement in the
LogStream function can't work. Finally, this is going to be somewhat
awkward for the user anyway. A log of operator<< are non-members,
with an ostream& non-const reference as first argument, so you can't
call them with a temporary as the left argument. (In your example call,
of course, you forgot to create the std::ostringstream anyway; it
won't compiler because there is no overload of << which takes a char
const[]
or a char const* as its left hand operand.)

There are work-arounds for almost all of these problems. Something
like:

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

handles all of the problems except the last; the last has to be handled
by the client, something like:

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

(The call to std::ostream::flush() returns a non-const reference to
the stream, which can be used to initialize further std::ostream&.
And while you can't initialize a non-const reference with a temporary,
you can call a non-const member function on it.)

The awkwardness of this for the client code makes me generally prefer a
more complex solution. I define a special LogStreamer class,
something like:

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

and

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

The client code can then write:

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

In my own code: the log object is always a singleton, the call is
through a macro, which passes __FILE__ and __LINE__ to the LogStream()
function, and the final target ostream is a special streambuf with a
special function, called by LogStream(), which takes a filename and a
line number, outputs them, along with the time stamp, at the start of
the next line output, and indents all other lines. A filtering
streambuf with something like:

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();
    }
};

(The function now(), of course, returns a std::string with the
timestamp. Or a struct tm, and you've written a << for tm.)

对风讲故事 2024-11-21 08:39:06

你的设计有问题。您不想接受流作为参数,要么接受字符串,要么让您的类表现为流(或两者)。

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

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

为此,只需重写 << 运算符即可。

You have a problem with your design. You don't want to accept a stream as a parameter, either accept a string, or make your class behave as a stream (or both).

If you make your object behave as a stream, then you do the following:

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

To do that, simply override the << operator.

江城子 2024-11-21 08:39:06

您的调用应该如下所示,

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

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

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

Your call should look like

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

since you need to create your stringstream object that you will be passing to the function.

This call implies that you already have a desired output stream so i'd also recommend you changing your class design to use operator<< for logging unless it is already overloaded.

人生戏 2024-11-21 08:39:06

您的函数调用将不起作用,因为 "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 &)< /code> 并让该函数的用户自己调用 stream.str()

Your function call won't work, as "MKLBSearchEngine::Search( " is of type const char* and that has no overload for the << operator. It won't work with std::string("MKLBSearchEngine::Search( ") either, as also std::string doesn't have such an operator. What you can do is call it with std::stringstream("MKLBSearchEngine::Search( "), which converts the first argument to a stream, such that the following operators work on this stream. But as others pointed out, you will have to make the function argument a const reference, as streams are not copyable (even then it would be quite inefficient). Also just writing a std::stringstream into the file won't do what you want (if it works anyway), instead you have to take its contents (the underlying std::string). So all in all your code should look like:

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

But you could also just use a LogString(const std::string &) and let the user of this function call stream.str() himself.

仙女 2024-11-21 08:39:06

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

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

这可能不会达到您所期望的效果(它可能会打印 i_Log 的地址,出于相当模糊的原因)。

如果您的目的是从字符串流中取出内容,这可能会满足您的要求:

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

You can't pass stream objects by value (since they are not copyable), so you need to pass by (and store) references:

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

This probably won't do what you're expecting though (it will probably print an address of i_Log, for rather obscure reasons).

If your intention is to take stuff OUT of the stringstream, this might do what you want:

i_Log.get( *m_FileHandle.rdbuf() );
风苍溪 2024-11-21 08:39:06

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

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

阅读有关 C++ 参考的更多信息。

You are passing std::stringstream instance by value. You want to avoid copying and pass it by reference (or pointer). For example:

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

Read more about C++ references.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文