Archived

This topic is now archived and is closed to further replies.

Kylotan

Nullstream implementation - comments?

Recommended Posts

Kylotan    9860
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 ]

Share this post


Link to post
Share on other sites
Brobanx    136
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).

Share this post


Link to post
Share on other sites
Kylotan    9860
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 ]

Share this post


Link to post
Share on other sites
dalleboy    324
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

Share this post


Link to post
Share on other sites
Fruny    1658
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 ]

Share this post


Link to post
Share on other sites
Kylotan    9860
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 ]

Share this post


Link to post
Share on other sites
Fruny    1658
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 ]

Share this post


Link to post
Share on other sites
dalleboy    324
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);
}

Share this post


Link to post
Share on other sites
Kylotan    9860
Well, I don''t know. I don''t expect the nullistream is as useful as the nullostream, since you''re either going to be populating variables with junk, stuck in infinite stream-reading loops, or both

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]

Share this post


Link to post
Share on other sites
DrPizza    160
This doesn't do the job?

    
namespace
{
#ifdef _WIN32
const char* nullFileName("C:\\NUL");
#else // _WIN32

const char* nullFileName("/dev/null");
#endif // _WIN32

}

ofstream nullStream(nullFileName);



[edited by - DrPizza on October 20, 2002 5:04:03 PM]

Share this post


Link to post
Share on other sites
Void    126
I wouldn''t use a streambuf for a logging stream.

Reason

The iostream does too much formatting. By the time the data gets to the streambuf, a lot of formatting code (which I don''t need) would have be executed. I want a logger with lowest overhead should I want to use it in production code.

My suggestion for improvements.

Use a decorator pattern for the streambuf. That way, you can derive classes that does output to file, output to console, debugger. You can chain the derive classes together to synchronize the logs.

You can also add a multithread lock before attempting to write any data if you are using a decorater pattern. I''m not sure if you can do it cleanly with just the streambuf. For me, I written a stream class that does not derive from STL. So I can stick the threading policy that does the locks before attempting any writing.

Share this post


Link to post
Share on other sites
Oluseyi    2103
quote:
Original post by Void
The iostream does too much formatting.

If you use its formatted I/O functionality (operator >>, operator << ). read and write don't do any formatting, IIRC.

[Edit: Need to fix the smiley parsing.]

[edited by - Oluseyi on October 21, 2002 10:22:16 AM]

Share this post


Link to post
Share on other sites
DrPizza    160
quote:
Original post by Kylotan
Does that work on MacOS 9 and earlier? PS2? Dreamcast? QNX?


QNX, I would imagine so, it strives to be reasonably *nixy. The others? Who knows. It'd be quite a deficit if they didn't.

And on the PS2 or Dreamcast, I would prefer to ditch the logging entirely, as I wouldn't care to waste the resources formatting the things written to the stream.


[edited by - DrPizza on October 21, 2002 11:53:19 AM]

Share this post


Link to post
Share on other sites
Kylotan    9860
I find that pretty funny that people are worrying about the overhead on a stream''s formatting. I mean, there''s only one or two of these streams per application, right? And you will be pretty much free to set the buffering how you want, and indeed to use the unformatted I/O functions if you desire. And you could imbue the stream with the standard C locale, if that speeds things up at all on the formatted functions.

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]

Share this post


Link to post
Share on other sites
DrPizza    160
quote:
Original post by Kylotan
I find that pretty funny that people are worrying about the overhead on a stream''s formatting.

I think the point is, why pay the price?

quote:
I mean, there''s only one or two of these streams per application, right?

I''m not sure why that matters.

quote:
And you will be pretty much free to set the buffering how you want,

Neither here nor there.

quote:
and indeed to use the unformatted I/O functions if you desire.

One would have thought that the main reason for using an iostream rather than something lower-level like a HANDLE or somesuch was so that one could use the formatted I/O operators.

quote:
And you could imbue the stream with the standard C locale, if that speeds things up at all on the formatted functions.

I don''t think it should make a significant difference.

Share this post


Link to post
Share on other sites
Kylotan    9860
quote:
Original post by DrPizza
I think the point is, why pay the price?

What price? Are they really all that expensive? Not in my experience...

quote:
One would have thought that the main reason for using an iostream rather than something lower-level like a HANDLE or somesuch was so that one could use the formatted I/O operators.

Or portability?

And you can mix formatted and unformatted, leaving the formatted ones for when you need it.

quote:
I don''t think it should make a significant difference.

So where is the slowdown? If it''s not the buffering, and not the locale translation, what is it? Virtual functions?



[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]

Share this post


Link to post
Share on other sites
DrPizza    160
quote:
Original post by Kylotan
What price?

Formatted I/O.

quote:
Are they really all that expensive? Not in my experience...

Compared to nothing at all?

Infinitely more expensive.

quote:
Or portability?

And you can mix formatted and unformatted, leaving the formatted ones for when you need it.

But have you?

And why pay the price at all?

quote:
So where is the slowdown? If it''s not the buffering, and not the locale translation, what is it? Virtual functions?

Formatted I/O, obviously.

Share this post


Link to post
Share on other sites