Why the constructor of std::ostream is protected?
Asked Answered
G

5

20

Why I can't just create an "empty" stream for my output like this

std::ostream out;

?

This rows is apparently illegal with both clang 3.4 and gcc 4.8.1 under linux with libstdc++, I really don't get why, I mean why I can't just create a stream out of nowhere and use it like I want to ? notice that std::ofstream out; is 100% ok instead. I just don't get the logic behind this. This is even stranger if you consider that after the creation I can just use this buffer and share a common buffer with other buffers with copyfmt so there is no real need for my std::ostream to be initialized to something right from the creation of the object to be useful.

Please don't deviate from this, I don't need stringstreams, I need ios streams because of what I have to do and because of the methods and the properties that they are offering.

Gilda answered 3/8, 2013 at 9:28 Comment(8)
What do you expect ostream to do?Appoint
@Appoint behave like std::cout or std::cerr ? be just like a stream for the output ?Gilda
I think he means "output to where". BTW - cout and cerr output to different locations. They just happen to be locations that by default print to the screenBilliards
@cluracan and what this location is ? I just don't care about the location, I need a stream for generic outputGilda
cerr prints to the "standard error stream". That stream can be intercepted and move to, e.g., a log file (even after compilation - you can do it when you run a program). cout prints to the "standard output stream". This too can be intercepted and moved to a file, or to be the input of another program etc. If you want to write to standard output, use cout. if you want to write to standard error stream, use cerr. If you want to write somewhere else, you need to first decide where that "somewhere else" is. tl;dr - use cout.Billiards
@cluracan in my view I need more than 1 stream because of the different kind of informations that I would like to output, I'm also seeing std::cout more like "the terminal" than a stream, If I just output everything to std::cout or std::cerr, than I have nothing left to analyze and everything is scattered in 2 buffersGilda
cout isn't the terminal. It's just the terminal unless the caller of your program (NOT YOU) decides otherwise. If you had two different ostreams writing to the same place (say, the terminal), they'd interfere with each other. It's like having two ints with the same address, that you use separately! No one would be able to trust a stream if some other part of the program could write to the exact same place whenever it wants.Billiards
@user2485710: You can create an std::ostream if you want to but you can't use the default construct. You'd need to pass in a pointer to a stream buffer (std::streambuf*) which can be null. For the streams to do anything, they need a stream buffer, however, which deals with sending the characters somewhere, e.g., a std::filebuf sends them to a file, a std::stringbuf sends them to a string, and user-defined stream buffers can send them wherever you wants them to send characters to, e.g., a GUI window or some area in memory.Stevens
C
19

I'll admit that I don't understand it either. I can't find any default constructor at all for std::istream, and I would think you would want one if you want to create a bidirectional stream, because of the strange way std::ios_base works: the constructor does not initialize anything, but the derived class must call std::ios_base::init explicitly in its constructor. When multiple inheritance is involved (i.e. bidirectional IO, where the class derives from both std::istream and std::ostream), I would expect only the most derived class to call std::ios_base::init. (In std::iostream, std::ios_base::init will be called twice.) In fact, before looking it up in the standard, I was about to answer that the default constructor was protected, because it didn't call std::ios_base::init, and using it directly, rather than in a derived class, would result in an uninitialized stream.

Anyhow, your immediate problem has a simple solution:

std::ostream out( NULL );

Also: the function you need to set up its sink later is the non-const version of rdbuf(), not copyfmt(). rdbuf() is used to read and to set the pointer to the streambuf, copyfmt() copies the formatting flags, but does not touch the pointer to streambuf.

So you can do things like:

std::ostream out( NULL );
//  ...
std::filebuf fileBuffer;
if ( filenameGiven ) {
    fileBuffer.open( filename.c_str(), std::ios_base::out );
}
if ( fileIsOpen() ) {
    out.rdbuf( &fileBuffer );
} else {
    out.rdbuf( std::cout.rdbuf() );
}

(I do this a lot. In fact, I thought that it was the usual idiom when you didn't know up front whether to output to a file or to std::cout.)

EDIT:

And yet another correction: the non-const version of rdbuf calls clear(), so you don't have to. (I knew I'd done this without calling clear(), but when I saw that init set badbit...)

Anyhow: the summary is: it's usually preferrable to pass a pointer to a valid streambuf to the constructor of std::ostream, but if you can't, it's perfectly valid to pass a null pointer, and set a valid pointer later using rdbuf(). And the answers which say otherwise are simply wrong.

Cappella answered 3/8, 2013 at 10:12 Comment(5)
finally :D . men the ios part of the standard library is a real journey for a programmer ...Gilda
The funny initialization dance happens between std::ios and std::istream/std::ostream/std::iostream: users don't have to deal with it unless they want to directly derive from std::ios! The reason that dance is done is to avoid the stream buffer to be set twice when constructing an std::iostream: std::ios::init() is just called from that class's default constructor. ... and that is actually also the reason for the default construct of std::ostream being protected: it doesn't touch the stream buffer in std::ios and leaves it uninitialized!Stevens
@Gilda It's actually a lot simpler than much of the rest of the standard library. From a user's point of view, of course, it's complicated, because it is doing a very complicated job.Cappella
@DietmarKühl That's what I thought to begin with. I can't find any default ctor for std::istream in C++11; the constructor ofstd::iostream is specified as calling both std::istream and std::ostream with the streambuf pointer (which will result in std::ios::init() being called twice). Can it be that we are both thinking of classical iostream?Cappella
@JamesKanze: You are right. ... and there is also no default constructor in C++2003 (I don't have a version of C++1998 to check there). That's weird although there shouldn't be too much of an issue as the stream buffer needs to be initialized eventually and it seems just to be mandated to be done through the std::istream branch.Stevens
P
10

std::ostream is conceptually abstract - you're not supposed to create them (but see other answers for more detail on that).

std::stringstream gives you everything std::ostream does because it derives from it. This also means that anywhere you want an std::ostream&, for example in function arguments, you can pass a std::stringstream object.

You say "there's no need for [it] to be initialised [...] to be useful". That makes no sense at all. You cannot use anything that hasn't been initialised: even ints.

Piero answered 3/8, 2013 at 9:35 Comment(8)
I forgot to specify that from my viewpoint an initialization to a particular value or state given from the user can be useless, that's what I meant with that words; but anyway, where I can find the hierarchy of ostream ?Gilda
please see this #18031857Gilda
std::ostream isn't abstract at all! It doesn't even have any virtual functions except it destructor. You can create an std::ostream by using the constructor which takes an std::streambuf*.Stevens
Alright, notionally abstract, rather than explicitly abstract. I'd argue the CTR that takes a streambuf is a factory.... :)Piero
@SteveL You'd argue wrong. It's perfectly correct, and actually quite frequent (at least in my work) to use instances of std::ostream. In fact, the classes which derive from std::ostream are only there for convenience; fundamentally, the only class you need is std::ostream. (And I don't understand where you see a factory. The constructor of std::ostream doesn't call new, ever.)Cappella
@JamesKanze - OK that's interesting, because I figured basic_ostream and basic_istream themselves as convenience "wrapper" classes over the facilities of ios and ios_base - and streambuf. On the factory front, what I meant was that the streambuf is the actual stream, and ostream an API over it (in essence - I know there's more to it than that). So, factory with a small 'f' :)Piero
@Gilda - hierarchy of ostream - cplusplus.com/reference/ostream/ostreamPiero
@SteveL basic_ostream and basic_istream have real behavior: formatting and parsing. ios_base doesn't really; it just provides some simple common functionality. And streambuf really defines a separate hierarchy, with its own behavior, used by basic_istream and basic_ostream (but there's nothing to prevent you from using it otherwise.Cappella
M
8

The default constructor of std::basic_ostream is protected because it generally doesn't make any sense to create an std::basic_ostream without setting its std::basic_streambuf and the default constructor actually doesn't do any initialization (see below). There is, however, another constructor taking a stream buffer as argument. If you really need an std::ostream which isn't set up to do anything, yet, you can use that constructor:

std::ostream out(0);

This std::ostream will have the std::ios_base::badbit set until a stream buffer is set up using std::ios::rdbuf().

The underlying reason for std::ostream's default constructor being protected is that it actually deliberately doesn't do anything useful! In particular, calling this constructor [from a further derived class] will not initialize the stream buffer at all: the standard doesn't mandate any behavior explicitly, i.e., the default constructor has implicitly the behavior of the generate default constructor (or, in C++2011 as if it is defined using = default). To get the stream buffer initialized, it would need to call std::ios::init(). The reason for this odd behavior is that constructing an object of the further derived class std::iostream would initialized the std::ios object twice, once through std::ostream and once through std::istream.

Unlike some of the other answers suggest, std::ostream isn't abstract at all. In fact, it only has exactly one virtual function anyway and that is its destructor (the destructor being virtual happens to be my fault; I'm not entirely convinced that it was really a good idea to force that).

Meshwork answered 3/8, 2013 at 10:15 Comment(5)
I think you misworded something. What does it mean to say "The default constructor is abstract"? Functions aren't abstract, classes are, and constructors can't be virtual either. The default constructor simply isn't there (and since there are other constructors, the compiler won't generate it either).Cappella
@JamesKanze: good point. I meant to write protected (will correct in a moment). Thanks!Stevens
You're post is exactly what I thought, until I looked it up in the standard. There is no default constructor, at least that I can find. What you describe makes better sense than what is in the standard (and it is what I thought too, until I looked it up), but it doesn't seem to be the case. (Maybe it was the case with classical, pre-standard IO stream, and we're both thinking of this.)Cappella
@JamesKanze: Awkward but you're right! There isn't even a protected default constructor. That means that standard libraries will have an extra constructor which users can't use (i.e., it takes a type users can't name) and which doesn't do anything. Still things to learn (or relearn more likely) about IOStreams! ;)Stevens
thanks for your contribute too, it's always nice to read something in the details. I will probably join comp.lang.c++ soon :DGilda
F
3

The std::ostream is a generalized stream class. Because of that it has no way of knowing what it is streaming to, which is the whole point of having a generalized class.

When you send data to a stream it doesn't actually store the data itself. It simply passes it forward to an associated buffer. Taking this into consideration, creating a generalized stream without assigning this buffer to it makes no sense, as the generalized class cannot create an empty generalized buffer. (The buffer itself needs to be a buffer of a concrete type, which is also understood by looking at the generalized buffer std::treambuf that doesn't have a public consstructor at all)

Compare this with the std::ofstream, which is a stream with a specific type of buffer, it is now possible for the ofstream to know what kind of buffer it uses, thus being able to instantiate a default std::filebuf.

To solve your concrete problem.

Create the buffer of your wanted type first, and then create the a generalized std::ostream with the buffer as a parameter. You can then at a later point connect to a file using std::filebuf::open().

Example:

std::filebuf fileBuffer;
std::ostream myOstream(&fileBuffer); // Hand over the address of the fileBuffer

fileBuffer.open("filename.txt", std::ios::out);
myOstream << "Text to file";
Fatma answered 3/8, 2013 at 9:45 Comment(1)
His whole point was that he didn't have the streambuf until later, so he couldn't do this. That may be a questionable architecture; the streambuf usually needs a lifetime at least as long as that of the istream, so one would expect it to be created first, but there are certainly exceptions to this rule.Cappella
B
2

stringstream is an ios stream.

But about your question:

An ostream writes somewhere. if you declare

std::ostream out;
out << "Hello, world!"<<std::endl

the "Hello, world!" should be written somewhere. But where? That depends on the implementation for each specific ostream. Yes, there is a buffer, but that buffer also depends on the specific implementation.

So when you say "I want an ostream" I have to ask - one which writes stuff... to where?

Given your answer, that will tell me which specific instance/implementation of ostream you need to use.

Billiards answered 3/8, 2013 at 9:34 Comment(12)
Well, if I write int a; I don't have to declare where to store the int, the compiler does for me, why a stream can't have a "default" "fallback" value and still be in a valid state ?Gilda
Because it has to print somewhere! And where will it print? If there was a default, it would be "I don't know where to print to, so I'll just throw away everything you write". That is the equivalent to your int example (if you could only write to an int and not read from it, like ostream). But that behaviour would be silly and most likely not what you want. Just tell us what you want to actually happen when you write something, and why cout isn't good enoughBilliards
scenario: I have a program written in C++ that also offers a wrapper to a scripting language, I need to store and output ( have a buffer basically ) about the state of the application, the errors and the debug messages, but I want to keep this 3 buffers separated for the C++ applications and the scripting wrapper, so I need a total of 6 buffers and I don't want them to be mixed together, I want them to never see one to another and I want to decide on what output I can redirect those 6 streams: what class or type you pick for that ?Gilda
Then have your class (?) receive and remember a POINTER (or reference) to an ostream where it will output whatever. Then when creating this class you can decide if you want to give it cout (to output to the "standard output", or ostringstream to use internally later, or ofstream to write to a file. You keep not saying WHERE you want the output to go! If you want the screen, you use cout. If you want the screen, but want to also "keep them separated" then explain what that means...Billiards
if i create a stringstream I don't have to make this kind of choices, why is that so hard for this part of the library to just create an object, let the object grow as the content is being inserted in the stream and let the user decide at some point in the code where to output stuff; why I need to set this kind of things upfront ? By the way my possible options are cout,cerr,clog and a file .Gilda
What? If you made a stringstream you DID make the choice! a stringstream IS A STREAM that writes to a string (memory). You decided you wanted to write to memory (not file, not "screen"). You decided. You probably don't realize this, but ostringstream is an ostream. If this is what you want (writing to memory) use ostringstream.Billiards
OK, so where does clog write to? a log file? so it's a file too? or somewhere else?Billiards
let us continue this discussion in chatBilliards
Unfortunately I can't chat right now, but in the end let's say that yes, I need clog to write to a file or to the standard output, what is the flexible solution in this case ?Gilda
have clog be a reference (or pointer) to an ostream, and use either cout or ofstream to initialize it.Billiards
@cluracan This answer is simply wrong. The streambuf pointer doesn't have to point anywhere; you can pass std::istream a null pointer, and set the streambuf later, using std::istream::rdbuf().Cappella
https://mcmap.net/q/615144/-why-the-constructor-of-std-ostream-is-protected this was what I wanted ... that's what I meant with an empty/default construction and the ability to pick a file or an std::cout based on my wishes.Gilda

© 2022 - 2024 — McMap. All rights reserved.