【问题标题】:Inheriting from std::basic_streambuf to write to a socket从 std::basic_streambuf 继承以写入套接字
【发布时间】:2017-06-06 01:06:41
【问题描述】:

我想写一个我自己的日志库,为日志条目发送到哪里提供抽象。

C++ 的 IO 库已经通过 std::stringstreamstd::fstream 提供了这种抽象。我还希望能够从/向套接字读取/写入。

我读到扩展标准库的正确方法是从std::basic_streambuf 继承。我不明白的是,如果像std::basic_filebuf 那样从std::basic_streambuf 继承,那么std::ifsreamstd::ofstreamstd::fstream 类的需求在哪里?我不能用std::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 );
    }
};

【问题讨论】:

  • 好消息,这实际上很容易做到。
  • 不错。一次写一些缓冲区怎么样?
  • 读/写缓冲区 goto streambuf::xsget() , -xsput() 这是套接字流的典型访问

标签: c++ inheritance stream iostream streambuf


【解决方案1】:

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

std::basic_streambuf 是一个较低级别的接口,用于从...某处读取或写入字符块。这就是你需要子类化和实现的东西。

而且,您走在正确的轨道上。 std::istreamstd::ostream 都有一个重载的构造函数,它接受一个指向流缓冲区的指针。所以,你的行动计划是:

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

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

我不能用一个实例来替换一些流的缓冲区吗? std::basic_streambuf 的子类

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

例如,std::ifstreamstd::istream 的子类,它使用指向从文件读取的内部流缓冲区的指针构造其超类。

随意创建您自己的std::istream 子类,它从您的流缓冲区子类和std::istream 相乘继承,首先构造流缓冲区子类,然后是std::istream

【讨论】:

  • 感谢您的帮助。请看我的编辑。它是否正确 ?在这里,我将有两个缓冲区,一个在我的 basic_sockstream 类的每个基类中。那是对的吗 ?当单个缓冲区支持两种操作时,为什么要有一个输入缓冲区和另一个输出缓冲区?还有一个问题:数据格式(字符串/数字转换等)与数据发送到的位置无关。那么,为什么没有一个“顶级”流类可以使用不同的 streambuf 实现,而不是像 stringstream 和 fstream 这样的多个“顶级”流类呢?
  • 不太正确。首先构造超类,使用指向尚未构造的类成员的指针。超类在类成员之前构造。尽管这不太可能导致任何实际问题,但这在技术上是一个错误,并且是未定义的行为。我引用多重继承是有原因的——您应该首先从流缓冲区继承,然后是流类,以便首先构造流缓冲区。而且,是的,你可以——而且你应该——对输入和输出操作使用单个流缓冲区。
  • 现在我不知道该怎么做。您会将streambuf 放在哪里?在“顶级”basic_sockstream 类中?在basic_sockstream 的构造函数中,我首先必须调用父构造函数并为它们提供一个streambuf,我当时没有这个@ 以转发给basic_istreambasic_ostream 的构造函数。另一方面,如果将缓冲区放入basic_isockstreambasic_osockstream,我最终将拥有两个缓冲区。
  • 正如我所解释的:多重继承。继承自 streambuf 和 std::ostreamclass basic_osockstream : public basic_sock_streambuf&lt; ... &gt;, public std::basic_ostream&lt; char_type, traits_type &gt;。构造函数:basic_osockstream(): parent(this).
  • 我明白了。但是,就像fstream 继承自 ifstreamofstream 一样,如果 basic_sockstream 继承自您刚刚描述的 basic_osockstream,并且还继承自等效的 basic_isockstream(它将对称地继承自 basic_sock_streambufbasic_istream),我会得到两个缓冲区,不是吗?
猜你喜欢
  • 2015-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多