继承自 std::basic_streambuf 以写入套接字

Inheriting from std::basic_streambuf to write to a socket

提问人:Virus721 提问时间:1/21/2017 最后编辑:Virus721 更新时间:1/22/2017 访问量:3855

问:

我想编写一个我自己的日志库,为日志条目发送到的任何地方提供抽象。

C++ 的 IO 库已经提供了这种抽象,带有 和 。我还希望能够从套接字读/写。std::stringstreamstd::fstream

我读到扩展标准库的正确方法是继承 .我不明白的是,如果从 like 继承,哪里需要 和 类?我不能将某个流的缓冲区替换为子类的实例,该子类的子类输出到我想要的位置吗?std::basic_streambufstd::basic_streambufstd::basic_filebufstd::ifsreamstd::ofstreamstd::fstreamstd::basic_streambuf

到目前为止,我已经做了以下工作,但我真的不确定我在做什么。以下设计是否正确?

template< typename char_type, typename traits_type = std::char_traits< char_type > >
class basic_sock_streambuf : public std::basic_streambuf< char_type, traits_type >
{
public:

    basic_sock_streambuf()
    {

    }

    ~basic_sock_streambuf()
    {

    }

    int overflow (int c = EOF)
    {
        fputc( c, stdout ); // Temporary.
        return traits_type::to_int_type( c );
    }

    int underflow()
    {
        return fgetc( stdout ); // Temporary.
    }

    int sync()
    {
        return 0;
    }
};

template< typename char_type, typename traits_type = std::char_traits< char_type > >
class basic_isockstream : public std::basic_istream< char_type, traits_type >
{
};

template< typename char_type, typename traits_type = std::char_traits< char_type > >
class basic_osockstream : public std::basic_ostream< char_type, traits_type >
{
};

template< typename char_type, typename traits_type = std::char_traits< char_type > >
class basic_socktream : public basic_isockstream< char_type, traits_type >, public basic_osockstream< char_type, traits_type >
{
private:

    typedef basic_isockstream< char_type, traits_type > iparent;

    typedef basic_osockstream< char_type, traits_type > oparent;

    basic_sock_streambuf< char_type, traits_type > sock_sb;

    std::basic_streambuf< char_type, traits_type > * old_isb;

    std::basic_streambuf< char_type, traits_type > * old_osb;

public:

    basic_socktream()
    {
        old_isb = iparent::rdbuf( & sock_sb );
        old_osb = oparent::rdbuf( & sock_sb );
    }

    ~basic_socktream() throw()
    {
        iparent::rdbuf( old_isb );
        oparent::rdbuf( old_osb );
    }
};

编辑:根据答案更新代码:

template<
    typename char_type,
    typename traits_type = std::char_traits< char_type > >
class basic_sockbuf :
    public std::basic_streambuf< char_type, traits_type >
{
public:

    basic_sockbuf()
    {
    }

    ~basic_sockbuf()
    {
    }

    int overflow( int c = EOF )
    {
        fputc( c, stdout ); // Temporary.
        return traits_type::to_int_type( c );
    }

    int underflow()
    {
        return fgetc( stdout ); // Temporary.
    }

    int sync()
    {
        return 0;
    }
};

template<
    typename char_type,
    typename traits_type = std::char_traits< char_type > >
class basic_isockstream :
    public std::basic_istream< char_type, traits_type >
{
private:

    typedef std::basic_istream< char_type, traits_type > parent;

    basic_sockbuf< char_type, traits_type > buffer;

public:

    basic_isockstream() :
        std::basic_istream< char_type, traits_type >::basic_istream(),
        buffer()
    {
        init( & buffer );
    }
};

template<
    typename char_type,
    typename traits_type = std::char_traits< char_type > >
class basic_osockstream :
    public std::basic_ostream< char_type, traits_type >
{
private:

    basic_sockbuf< char_type, traits_type > buffer;

public:

    basic_osockstream() :
        std::basic_ostream< char_type, traits_type >::basic_istream(),
        buffer()
    {
        init( & buffer );
    }
};

template<
    typename char_type,
    typename traits_type = std::char_traits< char_type > >
class basic_socktream :
    public std::basic_iostream< char_type, traits_type >,
    public basic_sockbuf< char_type, traits_type >
{
private:

    basic_sockbuf< char_type, traits_type > buffer;

public:

    basic_socktream() :
        std::basic_iostream< char_type, traits_type >::basic_iostream(),
        buffer()
    {
        std::basic_iostream< char_type, traits_type >::init( & buffer );
    }
};
C++ 继承流 IOstream Streambuf

评论

0赞 Captain Obvlious 1/21/2017
好消息是,这实际上很容易做到。
1赞 kyb 6/6/2017
好。一次写一些缓冲区怎么样?
0赞 Sam Ginrich 1/12/2022
读取/写入缓冲区 goto streambuf::xsget() , -xsput() 这是套接字流的典型访问

答:

9赞 Sam Varshavchik 1/21/2017 #1

std::istream并提供格式化的输入和输出操作。也就是说,将流转换为数字、字符串等/从数字、字符串等转换......std::ostream

std::basic_streambuf是一个较低级别的接口,用于读取或写入字符块或从中读取或写入字符块......地方。这就是您需要子类化和实现的内容。

而且,您走在正确的轨道上。两者都有一个重载构造函数,该构造函数采用指向流缓冲区的指针。因此,您的行动计划是:std::istreamstd::ostream

  1. 子类化并实现您的自定义 .std::basic_streambuf

  2. 使用指向流缓冲区的指针构造 a 或 a。std::istreamstd::ostream

我不能用 std::basic_streambuf 的子类

不,不是替换,而是构建一个。使用指向缓冲区的指针构造 a 或 a。您不会使用 ,而是使用流缓冲区构造的 和 。std::istreamstd::ostreamstd::[io]fstreamstd::istreamstd::ostream

例如,所有 a 都是一个子类,该子类构造其超类,其中包含指向从文件中读取的内部流缓冲区的指针。std::ifstreamstd::istream

您可以随意创建自己的子类 ,该子类从流缓冲区子类乘以继承,并且 首先构造流缓冲区子类,然后构造 .std::istreamstd::istreamstd::istream

评论

0赞 Virus721 1/21/2017
感谢您的帮助。请看我的编辑。这是正确的吗?在这里,我将有两个缓冲区,一个位于我的basic_sockstream类的每个基类中。这是对的吗?当单个缓冲区支持这两种操作时,为什么有一个用于输入的缓冲区和另一个用于输出的缓冲区?还有一个问题:数据格式(字符串/数字转换等)与数据发送到的位置无关。那么,为什么没有一个“顶级”流类可以使用不同的 streambuf 实现,而不是像 stringstream 和 fstream 这样的多个“顶级”流类呢?
0赞 Sam Varshavchik 1/21/2017
不太正确。首先构造超类,使用指向尚未构造的类成员的指针。超类在类成员之前构造。尽管这不太可能导致任何实际问题,但从技术上讲,这是一个错误,并且是未定义的行为。我引用多重继承是有原因的——你应该先从流缓冲区继承,然后再从流类继承,这样流缓冲区就会首先被构造出来。而且,是的,您可以 - 并且应该 - 使用单个流缓冲区进行输入和输出操作。
0赞 Virus721 1/21/2017
现在我不确定该怎么做。你会把?在“顶级”班级中?在 的构造函数中,我首先必须调用父构造函数,并为它们提供一个 ,我当时没有,要转发给 和 的构造函数。另一方面,如果将该缓冲区放入 和 中,我最终将拥有两个缓冲区。streambufbasic_sockstreambasic_sockstreamstreambufbasic_istreambasic_ostreambasic_isockstreambasic_osockstream
0赞 Sam Varshavchik 1/21/2017
正如我所解释的:多重继承。继承自 streambuf 和 。.构造 函数:。std::ostreamclass basic_osockstream : public basic_sock_streambuf< ... >, public std::basic_ostream< char_type, traits_type >basic_osockstream(): parent(this)
0赞 Virus721 1/21/2017
明白了。但是,就像继承自 和 一样,如果继承自您刚才描述的,并且还继承自等价物(将对称继承自 and),我会得到两个缓冲区,不是吗?fstreamifstreamofstreambasic_sockstreambasic_osockstreambasic_isockstreambasic_sock_streambufbasic_istream