Nullstream implementation - comments?

Started by
19 comments, last by Kylotan 21 years, 6 months ago
Basically I thought it would be handy to have an istream and ostream that effectively did nothing. Why? So that I can write a function that takes a stream as a parameter - as a logfile, for example - and be able to turn off that stream without having to do any checking inside the functions, or to rewrite the interface. So to turn off logging, I pass a nullostream instead of an ofstream or whatever. I thought I''d post these here so that anyone can use them, but more importantly for anyone to suggest corrections or amendments, as I''ve never done this sort of thing before. They seem to work, but by their very nature, are hard to test. I also haven''t done a nulliostream yet, because I''m not quite sure (a) whether I''ll ever need one, and (b) how it affects the implementation. Please also excuse the slightly erroneously-named namespace and my grouping of 4 classes in 2 files instead of 4 files.
  
// nulloutbuf.h ---------------------------------------


#include <streambuf> 

#include <locale>

namespace KyloSTL
{
    // MSVC 6 (or at least, my version of it) will NOT allow you to specify

    // these explicitly, but will compile happily with these using statements.

    // Hopefully this namespace pollution is confined to this namespace,

    // but who knows what MSVC decides.

    using std::char_traits;
    using std::basic_streambuf;
    using std::basic_ostream;

    // Null output stream buffer -----------------------------------------

    template <class charT, class traits = char_traits<charT> >
    class basic_nulloutbuf : public basic_streambuf<charT, traits>
    {
    protected:
        virtual int_type overflow(int_type c)
        {
            // Return not_eof, as we never need to stop writing

            return traits::not_eof(c);
        }
    };

    typedef basic_nulloutbuf<char>    nulloutbuf;
    typedef basic_nulloutbuf<wchar_t> wnulloutbuf;

    // Null output stream ----------------------------------------------------

    template <class charT, class traits = char_traits<charT> >
    class basic_nullostream : public basic_ostream<charT, traits>
    {
    protected:
        basic_nulloutbuf<charT, traits> buf;
    public:
        basic_nullostream() : basic_ostream<charT, traits>(&buf) {}
    };

    typedef basic_nullostream<char>    nullostream;
    typedef basic_nullostream<wchar_t> wnullostream;

} // end KyloSTL namespace





// nullinbuf.h ------------------------------------


#include <streambuf>

#include <locale>

namespace KyloSTL
{
    // MSVC 6 (or at least, my version of it) will NOT allow you to specify

    // these explicitly, but will compile happily with these using statements.

    // Hopefully this namespace pollution is confined to this namespace,

    // but who knows what MSVC decides.

    using std::char_traits;
    using std::basic_streambuf;
    using std::basic_istream;

    // Null input stream buffer ------------------------------------------

    template <class charT, class traits = char_traits<charT> >
    class basic_nullinbuf : public basic_streambuf<charT, traits>
    {
    private:
        // This is what will be ''read'' each time

        int_type theValue;

    protected:
        // Always the same character...

        virtual int_type underflow()
        { return theValue; }

        // No buffer, so we get this direct from underflow and don''t

        // need all the traits conversion trickery to change it to an

        // int_type

        virtual int_type uflow()
        { return underflow(); }

    public:
        basic_nullinbuf(int_type v) : theValue(v) {}
    };

    typedef basic_nullinbuf<char>    nullinbuf;
    typedef basic_nullinbuf<wchar_t> wnullinbuf;


    // Null input stream -----------------------------------------------------

    template <class charT, class traits = char_traits<charT> >
    class basic_nullistream : public basic_istream<charT, traits>
    {
    protected:
        basic_nullinbuf<charT, traits> buf;
    public:
        // Hmm... initialisation order issue? Dunno...

        basic_nullistream(int_type v = 0) : buf(v), basic_istream<charT, traits>(&buf) {};
    };

    typedef basic_nullistream<char>    nullistream;
    typedef basic_nullistream<wchar_t> wnullistream;

} // end KyloSTL namespace

  
[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]
Advertisement
This was a really good idea. Keep up the good work.
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
I noticed your little comments about "MSVC 6 (or at least, my version of it) will NOT allow you to specify these explicitly, but will compile happily with these using statements. Hopefully this namespace pollution is confined to this namespace, but who knows what MSVC decides."

What version of MSVC, and what version of the STL are you using? I don''t experience these problems (I have Service Pack 5 and am using STLPort 4.5.3).
Whoa ! /dev/null and /dev/zero, cool ! Way to go Kylotan




Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Brobanx: I''m using MSVC6, Service pack 4 I think, with the latest non-beta STLPort installed, but using the default (yet patched) Dinkumware streams. If I change those first few lines to:

  //using std::char_traits;//using std::basic_streambuf;using std::basic_ostream;// Null output stream buffer -----------------------------------------template <class charT, class traits = std::char_traits<charT> >class basic_nulloutbuf : public std::basic_streambuf<charT, traits>  

..then I get this:
nulloutbuf.h(22) : error C2143: syntax error : missing '','' before ''<''
nulloutbuf.h(30) : see reference to class template instantiation ''KyloSTL::basic_nulloutbuf'' being compiled
nulloutbuf.h(22) : error C2059: syntax error : ''<''
nulloutbuf.h(30) : see reference to class template instantiation ''KyloSTL::basic_nulloutbuf'' being compiled
nulloutbuf.h(21) : error C2239: unexpected token ''>'' following declaration of ''charT''
nulloutbuf.h(21) : error C2059: syntax error : ''>''
nulloutbuf.h(21) : error C2371: ''charT'' : redefinition; different basic types
nulloutbuf.h(21) : see declaration of ''charT''

... and more errors. It might be fixed in SP5, but I don''t have that. Not much point changing something to require the latest service pack when it''s such a minor thing, anyway.

Fruny: this is platform-independent And if you used those devices with fstream, wouldn''t that probably give you a redundant buffer in the stream? (Yeah, I know this is all trivial stuff. )

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]
BTW: It would be great if the operator >> for different types always set the value to zero. I dunno if that would work or how it should be implemented. For example:

  nullistream stream;int i;float f;stream >> i;stream >> f;// Here both i and f would be equal to 0.// But in your implementation the streams fail bit is set.  

HTTP 500: Strike 1
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
quote:Original post by Kylotan
Fruny: this is platform-independent And if you used those devices with fstream, wouldn''t that probably give you a redundant buffer in the stream? (Yeah, I know this is all trivial stuff. )


Then this might help - according to my doc ( "Standard C++ IOStreams and Locales" ), calling setbuf(0,0) on a file stream buffer before any IO has been done on it disables buffering. Which makes sense for /dev/null and /dev/zero.

Grumph, you''ve done it. Now I feel like going back to my (already working) ogfxstream class and fleshing it out completely. (It does text output to a graphics environment, with the font renderer, currently GLUT or WGL, defined as a policy template class. )

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Well, yeah, that could probably be done with a suitably overloaded stream class. To be honest I didn''t forsee much use for the nullistream, but I could look into providing that functionality for all the basic types.

On the other hand, maybe I could just return a default constructed object of whichever type is requested? eg. Something like:


  template <class T>basic_nullistream<charT, traits>& operator>> (T& val){    val = T();    return *this;}  


If I was to provide something like that, I should probably remove the ability to specify the character that is returned by the streambuf... any comments?

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]
I was going to object, on the grounds that you are breaking stream semantics, but thought again an figured out that wasn''t such a bad idea after all.

Couple of points though - templated members (just like templated friends) are dangerous : the user can write a specialisation that will have private access, breaking encapsulation. The IOStream library explicitely provided overloads for the basic types.

I don''t quite know if existing overloads ( for custom class IO ) would work out of the box with a new stream class, but it would be worth testing before resorting to a template.

Another issue... that stream is of infinite length, so routines testing for end-of-stream will break. Ok, it''s no different from reading from /dev/zero, but still...


Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
The operator >> for basic_nullistream will not work if the stream object is sent to a function that takes a basic_istream as reference. For example:

  int DoStuff(istream& stream){   int i;   stream >> i;   // Will use istream::operator >> and not the nullistream::operator >>   return i;}int main(){   nullistream stream;   return DoStuff(stream);}  
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]

This topic is closed to new replies.

Advertisement