Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting from std::basic_streambuf to write to a socket

I would like to write a logging library of my own that provides abstraction for wherever the log entries are sent to.

The IO library of C++ already provides that kind of abstraction with std::stringstream and std::fstream. I would also like to be able to read/write from/to a socket.

I read that the proper way of extending the standard library is to inherit from std::basic_streambuf. What I don't understand is, if inheriting from std::basic_streambuf like std::basic_filebuf does, where is the need for the std::ifsream, std::ofstream and std::fstream classes ? Can't I just replace the buffer of some stream with a instance of a subclass of std::basic_streambuf which outputs where I want it to ?

So far I have done the following, but I really am not sure about what I'm doing. Is the following design correct ?

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

EDIT : Code updated based on answers :

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 );
    }
};
like image 244
Virus721 Avatar asked Jan 20 '17 23:01

Virus721


1 Answers

std::istream and std::ostream provide formatted input and output operations. That is, converting the stream to/from numbers, strings, etc...

std::basic_streambuf is a lower-level interface that reads or writes chunks of characters to or from ...somewhere. That's what you need to subclass and implement.

And, you're on the right track. Both std::istream and std::ostream have an overloaded constructor that take a pointer to a stream buffer. So, your plan of action is:

  1. Subclass and implement your custom std::basic_streambuf.

  2. Construct a std::istream or a std::ostream using a pointer to your stream buffer.

Can't I just replace the buffer of some stream with a instance of a subclass of std::basic_streambuf

No, not replace, but construct one. You construct a std::istream or a std::ostream, using a pointer to your buffer. You will not use a std::[io]fstream, but rather std::istream and std::ostream, constructed using your stream buffer.

All that a std::ifstream is, for example, is a subclass of std::istream that constructs its superclass with a pointer to an internal stream buffer that reads from a file.

Feel free to create your own subclass of std::istream, that multiply-inherits from your stream buffer subclass, and std::istream, constructs the stream buffer subclass first, then the std::istream.

like image 59
Sam Varshavchik Avatar answered Nov 13 '22 12:11

Sam Varshavchik