138

First off, it may seem that I'm asking for subjective opinions, but that's not what I'm after. I'd love to hear some well-grounded arguments on this topic.


In the hope of getting some insight into how a modern streams / serialization framework ought to be designed, I recently got myself a copy of the book Standard C++ IOStreams and Locales by Angelika Langer and Klaus Kreft. I figured that if IOStreams wasn't well-designed, it wouldn't have made it into the C++ standard library in the first place.

After having read various parts of this book, I am starting to have doubts if IOStreams can compare to e.g. the STL from an overall architectural point-of-view. Read e.g. this interview with Alexander Stepanov (the STL's "inventor") to learn about some design decisions that went into the STL.

What surprises me in particular:

  • It seems to be unknown who was responsible for IOStreams' overall design (I'd love to read some background information about this — does anyone know good resources?);

  • Once you delve beneath the immediate surface of IOStreams, e.g. if you want to extend IOStreams with your own classes, you get to an interface with fairly cryptic and confusing member function names, e.g. getloc/imbue, uflow/underflow, snextc/sbumpc/sgetc/sgetn, pbase/pptr/epptr (and there's probably even worse examples). This makes it so much harder to understand the overall design and how the single parts co-operate. Even the book I mentioned above doesn't help that much (IMHO).


Thus my question:

If you had to judge by today's software engineering standards (if there actually is any general agreement on these), would C++'s IOStreams still be considered well-designed? (I wouldn't want to improve my software design skills from something that's generally considered outdated.)

Machavity
  • 30,841
  • 27
  • 92
  • 100
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
  • 8
    Interesting Herb Sutter's opinion http://stackoverflow.com/questions/2485963/c-alignment-when-printing-cout/2486085#2486085 :) Too bad that guy left SO after only a few days of participation – Johannes Schaub - litb May 02 '10 at 10:45
  • 5
    Is there anyone else who sees a mixing of concerns in the STL streams? A stream is normally designed to read or writes bytes and nothing else. A thing that can read or write specific data types is a formatter (that may but need not to use a stream to read/write the formatted bytes). Mixing both into one class makes it even more complex to implement own streams. – mmmmmmmm May 02 '10 at 15:11
  • 5
    @rsteven, there is a separation of those concerns. `std::streambuf` is the base-class for reading and writing bytes, and `istream` / `ostream` is for formatted in- and output, taking a pointer to `std::streambuf` as its destination/source. – Johannes Schaub - litb May 02 '10 at 15:28
  • 1
    @litb: But is it possible to switch the streambuf that is used by the stream (formatter)? So maybe I want to use the STL formatting but want to write the data via a specific streambuf? – mmmmmmmm May 02 '10 at 16:40
  • 2
    @rstevens, `ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";` – Johannes Schaub - litb May 02 '10 at 16:44
  • (An intermittent thank you @ everyone for the great feedback to this question so far!) – stakx - no longer contributing May 02 '10 at 21:48
  • 1
    @JohannesSchaub-litb "_std::streambuf is the base-class for reading and writing bytes_" Wrong. `std::streambuf` supports text I/O. – curiousguy Jul 26 '12 at 10:32
  • 1
    @curiousguy: Other than the fact it can work with `wchar_t`, I see no evidence it can do any formatting or any other text-related activities. Why do you say it's for text I/O? The _intent_ appears that it's for bytes (or double-bytes). – Mooing Duck Aug 05 '14 at 16:18
  • 1
    @MooingDuck Support for text mode. – curiousguy Aug 06 '14 at 00:34
  • 1
    @curiousguy: `std::streambuf` doesn't have support for text mode as far as I can tell. (oh, `basic_filebuf` does, so you're right) – Mooing Duck Aug 06 '14 at 00:37
  • Waaat, not closed as subjective yet?! Flagging! – mlvljr Apr 28 '18 at 11:05
  • 1
    @mlvljr: This question was asked in the past golden age of SO where questions such as this one were still welcome. I'm sure that today, it would get closed as off-topic very quickly. :-( – stakx - no longer contributing Apr 28 '18 at 11:36
  • @stakx I know -- I was there at the times when Bill the Lizard, Pax Diablo and folks were the ones answering and moderating (I assume) :) – mlvljr Apr 30 '18 at 15:55

11 Answers11

47

Regarding who designed them, the original library was (not surprisingly) created by Bjarne Stroustrup, and then reimplemented by Dave Presotto. This was then redesigned and reimplemented yet again by Jerry Schwarz for Cfront 2.0, using the idea of manipulators from Andrew Koenig. The standard version of the library is based on this implementation.

Source "The Design & Evolution of C++", section 8.3.1.

Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
  • 3
    @Neil - nut what's your opinion of the design? Based on your other answers, many people would love to hear your opinion... – DVK May 02 '10 at 10:24
  • 1
    @DVK Just posted my opinion as a separate answer. –  May 02 '10 at 10:34
  • 2
    Just found a transcript of an interview with Bjarne Stroustrup where he mentions some bits and pieces of IOStreams history: http://www2.research.att.com/~bs/01chinese.html (this link seems to be temporarily broken right now, but you can try Google's page cache) – stakx - no longer contributing May 02 '10 at 11:48
  • 2
    Updated link: http://www.stroustrup.com/01chinese.html . – FrankHB May 06 '15 at 09:09
39

Several ill-conceived ideas found their way into the standard: auto_ptr, vector<bool>, valarray and export, just to name a few. So I wouldn't take the presence of IOStreams necessarily as a sign of quality design.

IOStreams have a checkered history. They are actually a reworking of an earlier streams library, but were authored at a time when many of today's C++ idioms didn't exist, so the designers didn't have the benefit of hindsight. One issue that only became apparent over time was that it is almost impossible to implement IOStreams as efficiently as C's stdio, due to the copious use of virtual functions and forwarding to internal buffer objects at even the finest granularity, and also thanks to some inscrutable strangeness in the way locales are defined and implemented. My memory of this is quite fuzzy, I'll admit; I remember it being the subject of intense debate some years ago, on comp.lang.c++.moderated.

Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • 3
    Thank you for your input. I'll browse the `comp.lang.c++.moderated` archive and post links at the bottom of my question if I find something valuable. -- Besides, I dare disagree with you on `auto_ptr`: After reading Herb Sutter's _Exceptional C++_ it seems like a very useful class when implementing the RAII pattern. – stakx - no longer contributing May 02 '10 at 10:29
  • 5
    @stakx: Nevertheless it is getting deprecated and superseded by `unique_ptr` with clearer and more powerful semantics. – UncleBens May 02 '10 at 10:47
  • 1
    @UncleBens: Thanks for that pointer to `unique_ptr`. I am continually amazed at what the Boost libraries come up with. (Unfortunately I'm still not nearly as familiar with them as I'd like to be.) – stakx - no longer contributing May 02 '10 at 10:59
  • 3
    @UncleBens `unique_ptr` requires rvalue reference. So at this point `auto_ptr` is very powerful pointer. – Artyom May 02 '10 at 11:53
  • 7
    But `auto_ptr` has screwed copy/assignment semantics which make it a niche for dereferencing bugs... – Matthieu M. May 02 '10 at 13:17
  • 3
    @Matthieu M.: That said, it's the best solution we have under the current standard. It's not poorly designed -- you just have to be aware of how it operates. – Billy ONeal Aug 29 '10 at 02:29
  • 3
    @Billy: I would have prefer `boost::scoped_ptr` semantics, I prefer not to have a copy constructor if it doesn't behave like a copy constructor. You can always use `swap` to exchange (explicitly) the contents of two of them, or another aptly named method. – Matthieu M. Aug 29 '10 at 11:07
  • 1
    @Matthieu: Unless you're wrapping a polymorphic interface you're using to fake an Operating System dependency. Then you cannot use anything like `scoped_ptr`. I Agree that `auto_ptr`'s semantics are strange. That doesn't mean it's designed poorly though. – Billy ONeal Aug 29 '10 at 14:32
  • 2
    what's so terrible about `vector`? – SingleNegationElimination Oct 30 '10 at 08:56
  • 1
    Apart from being deprecated, you mean? See [here](http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=98) and [here](http://en.wikipedia.org/wiki/Vector_(C%2B%2B%29#vector.3Cbool.3E_specialization). – Marcelo Cantos Oct 30 '10 at 09:14
  • 6
    @TokenMacGuy: it's not a vector, and it doesn't store bools. Which makes it somewhat misleading. ;) – jalf Oct 30 '10 at 12:17
  • 2
    @TokenMacGuy it is required to be bitpacked. And this in turn means that not every entry in it has a distinct memory address, so you can't give out a normal reference to a contained bool. Vector should'nt have been bitpacked, but instead a special class for BitPackedVectors should have been created. – CodesInChaos Oct 31 '10 at 09:32
31

If you had to judge by today's software engineering standards (if there actually is any general agreement on these), would C++'s IOStreams still be considered well-designed? (I wouldn't want to improve my software design skills from something that's generally considered outdated.)

I would say NO, for several reasons:

Poor error handling

Error conditions should be reported with exceptions, not with operator void*.

The "zombie object" anti-pattern is what causes bugs like these.

Poor separation between formatting and I/O

This makes stream objects unnecessary complex, as they have to contain extra state information for formatting, whether you need it or not.

It also increases the odds of writing bugs like:

using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops!  Forgot to set the stream back to decimal mode.

If instead, you wrote something like:

cout << pad(to_hex(x), 8, '0') << endl;

There would be no formatting-related state bits, and no problem.

Note that in "modern" languages like Java, C#, and Python, all objects have a toString/ToString/__str__ function that is called by the I/O routines. AFAIK, only C++ does it the other way around by using stringstream as the standard way of converting to a string.

Poor support for i18n

Iostream-based output splits string literals into pieces.

cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;

Format strings put whole sentences into string literals.

printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);

The latter approach is easier to adapt to internationalization libraries like GNU gettext, because the use of whole sentences provides more context for the translators. If your string formatting routine supports re-ordering (like the POSIX $ printf parameters), then it also better handles differences in word order between languages.

EFraim
  • 12,811
  • 4
  • 46
  • 62
dan04
  • 87,747
  • 23
  • 163
  • 198
  • 4
    Actually, for i18n, replacements should be identified by positions (%1, %2, ..), as a translation may require to change parameter order. Otherwise, I fully agree - +1. – peterchen Jul 05 '10 at 01:03
  • 5
    @peterchen: That's what the POSIX `$` specifiers for `printf` are. – jamesdlin Jul 05 '10 at 01:15
  • Regarding your last point about %s-style printing, it suffers from the same problem as printf does, that is, it's inherently type unsafe. This isn't as much of a problem in Python, which uses Duck Typing extensively, and in Java or C#, where the environment can catch type errors at runtime. – Lie Ryan Oct 30 '10 at 09:31
  • 2
    The problem isn't format strings, it's that C++ has non-typesafe varargs. – dan04 Oct 30 '10 at 14:33
  • 5
    As of C++11 it now has typesafe varargs. – Mooing Duck Aug 05 '14 at 16:22
  • 2
    IMHO the 'extra state information' is the worst issue. cout is a global; attaching formatting flags to it makes those flags global, and when you consider that most uses of them have an intended scope of a few lines, that's pretty awful. It would have possible to fix that with a 'formatter' class, that binds to an ostream but keeps its own state. And, things done with cout generally look terrible compared to the same thing done with printf (when that is possible).. – greggo Nov 20 '15 at 23:33
  • 2
    `std::to_string` is also a thing in C++11. – Alyssa Haroldsen Sep 14 '16 at 05:43
19

My opinion of C++ iostreams has improved substantially over time, particularly after I started to actually extend them by implementing my own stream classes. I began to appreciate the extensibility and overall design, despite the ridiculously poor member function names like xsputn or whatever. Regardless, I think I/O streams are a massive improvement over C stdio.h, which has no type safety and is riddled with major security flaws.

I think the main problem with IO streams is that they conflate two related but somewhat orthogonal concepts: textual formatting and serialization. On the one hand, IO streams are designed to produce a human-readable, formatted textual representation of an object, and on the other hand, to serialize an object into a portable format. Sometimes these two goals are one and the same, but other times this results in some seriously annoying incongruities. For example:

std::stringstream ss;
std::string output_string = "Hello world";
ss << output_string;

...

std::string input_string;
ss >> input_string;
std::cout << input_string;

Here, what we get as input is not what we originally outputted to the stream. This is because the << operator outputs the entire string, whereas the >> operator will only read from the stream until it encounters a whitespace character, since there's no length information stored in the stream. So even though we output a string object containing "hello world", we're only going to input a string object containing "hello". So while the stream has served its purpose as a formatting facility, it has failed to properly serialize and then unserialize the object.

You might say that IO streams weren't designed to be serialization facilities, but if that's the case, what are input streams really for? Besides, in practice I/O streams are often used to serialize objects, because there are no other standard serialization facilities. Consider boost::date_time or boost::numeric::ublas::matrix, where if you output a matrix object with the << operator, you'll get the same exact matrix when you input it using the >> operator. But in order to accomplish this, the Boost designers had to store column count and row count information as textual data in the output, which compromises the actual human-readable display. Again, an awkward combination of textual formatting facilities and serialization.

Note how most other languages separate these two facilities. In Java, for example, formatting is accomplished through the toString() method, while serialization is accomplished through the Serializable interface.

In my opinion, the best solution would have been to introduce byte based streams, alongside the standard character based streams. These streams would operate on binary data, with no concern for human-readable formatting/display. They could be used solely as serialization/deserialization facilities, to translate C++ objects into portable byte sequences.

Charles Salvia
  • 52,325
  • 13
  • 128
  • 140
  • thanks for answering. I could well be wrong about this, but concerning your last point (byte-based vs. character-based streams), isn't IOStream's (partial?) answer to this the separation between _stream buffers_ (character conversion, transport, and buffering) and _streams_ (formatting / parsing)? And couldn't you create new stream classes, ones that are meant solely for (machine-readable) serializing & deserializing and others that are uniquely geared towards (human-readable) formatting & parsing? – stakx - no longer contributing Oct 30 '10 at 09:32
  • @stakx, yes, and in fact, I have done this. It's slightly more annoying than it sounds, since `std::char_traits` can't be portably specialized to take an `unsigned char`. However, there are workarounds, so I guess extensibility comes to the rescue once again. But I think the fact that byte-based streams aren't standard is a weakness of the library. – Charles Salvia Oct 30 '10 at 09:52
  • 4
    Also, implementing binary streams requires you to implement new stream classes *and* new buffer classes, since formatting concerns aren't entirely separated from `std::streambuf`. So, basically the only thing you're extending is the `std::basic_ios` class. So there's a line where "extending" crosses over into "completely reimplementing" territory, and creating a binary stream from the C++ I/O stream facilities seems to approach that point. – Charles Salvia Oct 30 '10 at 10:01
  • well said & exactly what I suspected. And the fact that both C and C++ go to great lengths to *not* make guarantees about specific bit widths and representations can indeed become problematic when it comes to doing I/O. – stakx - no longer contributing Oct 30 '10 at 10:18
  • 1
    "_to serialize an object into a portable format._" no, they were never intended to support that – curiousguy Jul 26 '12 at 10:45
  • I still run into problems when using the IOStream. Many Besides the weird names Things get even worse when one uses the wchar_t variants. The wifstream does a translation to multibyte while wstringstream does not – gast128 Aug 17 '15 at 17:03
17

I'm posting this as a separate answer because it is pure opinion.

Performing input & output (particularly input) is a very, very hard problem, so not surprisingly the iostreams library is full of bodges and things that with perfect hindsight could have been done better. But it seems to me that all I/O libraries, in whatever language are like this. I've never used a programming language where the I/O system was a thing of beauty that made me stand in awe of its designer. The iostreams library does have advantages, particularly over the C I/O library (extensibility, type-safety etc.), but I don't think anyone is holding it up as an example of great OO or generic design.

13

i always found C++ IOStreams ill-designed: their implementation makes it very difficult to properly define a new type a stream. they also mix io features and formatting features (think about manipulators).

personally, the best stream design and implementation i have ever found lies in the Ada programming language. it is a model in decoupling, a joy to create new type of streams, and output functions always work regardless of the stream used. this is thank to a least common denominator: you output bytes to a stream and that's it. stream functions take care of putting the bytes into the stream, it is not their job to e.g. format an integer into hexadecimal (of course, there is a set of type attributes, equivalent to a class member, defined for handling formatting)

i wish C++ was as simple regarding to streams...

Adrien Plisson
  • 22,486
  • 6
  • 42
  • 73
  • The book I mentioned explains the basic IOStreams architecture as follows: There is a _transport layer_ (the stream buffer classes) and a _parsing/formatting layer_ (the stream classes). The former are responsible for reading/writing characters from/to a bytestream, while the latter are responsible for parsing characters or serializing values into characters. This seems clear enough, but I'm not sure if these concerns are truly clearly separated in reality, esp. when locales come into play. -- I also agree with you on the difficulty of implementing new streams classes. – stakx - no longer contributing May 02 '10 at 10:51
  • " mix io features and formatting features" <-- What's wrong about that? That's kind of the point of the library. Regarding making new streams, you should make a streambuf instead of a stream and construct a plain stream around the streambuf. – Billy ONeal May 03 '10 at 17:20
  • it seems answers to this question made me understand something that i was never explained: i should derive a streambuf instead of a stream... – Adrien Plisson May 04 '10 at 04:58
  • @stakx: If the streambuf layer did what you said, it would be fine. But the conversion between character sequence and byte are all mixed up with the actual I/O (file, console, etc). There's no way to perform the file I/O without also doing the character conversion, which is very unfortunate. – Ben Voigt Jan 21 '18 at 21:48
10

I think IOStreams design is brilliant in terms of extendability and usefulness.

  1. Stream buffers: take a look on boost.iostream extensions: create gzip, tee, copy streams in few lines, create special filters and so on. It would not be possible without it.
  2. Localization integration and formatting integration. See what can be done:

    std::cout << as::spellout << 100 << std::endl;
    

    Can print: "one hundred" or even:

    std::cout << translate("Good morning")  << std::endl;
    

    Can print "Bonjour" or "בוקר טוב" according to the locale imbued to std::cout!

    Such things can be done just because iostreams are very flexible.

Could it be done better?

Of course it could! In fact there are many things that could be improved...

Today it is quite painful to derive correctly from stream_buffer, it is quite non-trivial to add additional formatting information to stream, but possible.

But looking back many years ago I still the library design was good enough to be about to bring many goodies.

Because you can't always see the big picture, but if you leave points for extensions it gives you much better abilities even in points you didn't think about.

Artyom
  • 31,019
  • 21
  • 127
  • 215
  • 5
    Can you provide a comment as to why your examples for point 2 would be better than simply using something like `print (spellout(100));` and `print (translate("Good morning"));` This would seem like a good idea, as this decouples formatting and i18n from I/O. – Schedler May 04 '10 at 14:45
  • 3
    Because it can be translated according to langauage imbued into stream. i.e.: `french_output << translate("Good morning")` ; `english_output << translate("Good morning")` would give you: "Bonjour Good morning" – Artyom May 04 '10 at 19:12
  • 4
    Localisation is much harder when you need to do '<<"text"< – Martin Beckett Jul 05 '10 at 00:58
  • @Martin Beckett I know, take a look on Boost.Locale library, what happens that in such case you do `out << format("text {1}") % value` and it may be translated to `"{1} translated"`. So it works fine `;-)` . – Artyom Jul 05 '10 at 07:19
  • 18
    What "can be done" isn't very relevant. You're a programmer, anything *can be done* with enough effort. But IOStreams makes it awfully painful to achieve most of what *can be done*. And you usually get lousy performance for your trouble. – jalf Oct 31 '10 at 13:48
  • @Artonym: I still don't see why `english_output << translate("Good morning")` is more efficient can-be-done'esque than `english_output(translate("Good morning"))` – Sebastian Mach Oct 16 '18 at 10:19
2

(This answer is just based on my opinion)

I think that IOStreams are much more complex than their function equivalents. When I write in C++, I still use the cstdio headers for "old-style" I/O, which I find much more predictable. On a side note, (though it isn't really important; the absolute time difference is negligible) IOStreams have been proven on numerous occasions to be slower than C I/O.

Delan Azabani
  • 79,602
  • 28
  • 170
  • 210
  • I think you mean "function" rather than "functional". functional programming produces code thats even worse looking that generic programming. – Chris Becke May 02 '10 at 10:22
  • Thanks for pointing that mistake out; I've edited the answer to reflect the correction. – Delan Azabani May 02 '10 at 10:24
  • 5
    IOStreams would almost certainly have to be slower than classic stdio; if I was given the task to design an extensible and easy-to-use I/O streams framework, I would probably judge speed secondary, given that the real bottlenecks will likely be file I/O speed or network traffic bandwidth. – stakx - no longer contributing May 02 '10 at 10:37
  • 1
    I agree that for I/O or network the computational speed does not matter that much. However remember that the C++ for numeric/string conversion is using `sstringstream`. I think speed does matter, though it's secondary. – Matthieu M. May 02 '10 at 13:21
  • @Matthieu M.: Most of the time you don't need to do the kinds of conversions you use stringstream for unless you are doing i/o anyway. – Billy ONeal May 03 '10 at 17:22
  • 1
    @stakx file I/O and network bottlenecks are a function of 'per byte' costs which are quite small, and driven dramatically down by technology improvements. Also, given DMA, these overheads don't take away CPU time from other threads on the same machine. So, if you are doing formatted output, the cost of doing that efficiently vs. not, can easily be significant (at least, not overshadowed by disk or network; more likely it is overshadowed by other processing in the app). – greggo Nov 23 '15 at 22:17
2

I always run into surprises when using the IOStream.

The library seems text oriented and not binary oriented. That may be the first surprise: using the binary flag in file streams is not sufficient to get binary behavior. User Charles Salvia above has observed it correctly: IOStreams mixes formatting aspects (where you want pretty output, e.g. limited digits for floats) with serialization aspects (where you do not want information loss). Probably it would be good to separate these aspects. Boost.Serialization does this half. You have a serialize function which routes to the inserters and extractors if you want. There already you have the tension between both aspects.

Many functions have also confusing semantics (e.g. get, getline, ignore and read. Some extract the delimiter, some don't; also some set eof). Further on some mention the weird function names when implementing a stream (e.g. xsputn, uflow, underflow). Things get even worse when one uses the wchar_t variants. The wifstream does a translation to multibyte while wstringstream does not. Binary I/O does not work out of the box with wchar_t: you have the overwrite the codecvt.

The c buffered I/O (i.e. FILE) is not as powerful as its C++ counterpart, but is more transparent and has much less counter intuitive behavior.

Still every time when I stumble upon the IOStream, I get attracted to it like a moth to fire. Probably it would be a good thing if some really clever guy would have a good look at the overall architecture.

gast128
  • 1,223
  • 12
  • 22
1

C++ iostreams have a lot of flaws, as noted in the other responses, but I'd like to note something in its defense.

C++ is virtually unique among languages in serious use that makes variable input and output straightforward for beginners. In other languages, user input tends to involve type coercion or string formatters, while C++ makes the compiler do all the work. The same is largely true for output, although C++ isn't as unique in this regard. Still, you can do formatted I/O pretty well in C++ without having to understand classes and object-oriented concepts, which is at pedagogically useful, and without having to understand format syntax. Again, if you're teaching beginners, that's a big plus.

This simplicity for beginners comes at a price, which can make it a headache for dealing with I/O in more complex situations, but hopefully by that point the programmer has learned enough to be able to deal with them, or at least turned old enough to drink.

user2310967
  • 285
  • 2
  • 5
0

I cannot help answering the first part of the question (Who did that?). But it was answered in other posts.

As to the second part of the question (Well designed?), my answer is a resounding "No!". Here a little example which makes me shake my head in disbelief since years:

#include <stdint.h>
#include <iostream>
#include <vector>

// A small attempt in generic programming ;)
template <class _T>
void ShowVector( const char *title, const std::vector<_T> &v)
{
    std::vector<_T>::const_iterator iter;
    std::cout << title << " (" << v.size() << " elements): ";
    for( iter = v.begin(); iter != v.end(); ++iter )
    {
        std::cout << (*iter) << " ";
    }
    std::cout << std::endl;
}
int main( int argc, const char * argv[] )
{
    std::vector<uint8_t> byteVector;
    std::vector<uint16_t> wordVector;
    byteVector.push_back( 42 );
    wordVector.push_back( 42 );
    ShowVector( "Garbled bytes as characters output o.O", byteVector );
    ShowVector( "With words, the numbers show as numbers.", wordVector );
    return 0;
}

The above code produces nonsense due to iostream design. For some reasons beyond my grasp, they treat uint8_t bytes as characters, while larger integral types are treated like numbers. Q.e.d. Bad design.

There is also no way I can think of to fix this. The type could as well be a float or a double instead... so a cast to 'int' to make silly iostream understand that numbers not chars are the topic will not help.

IOStream design is flawed as it does not give the programmer a means to state HOW an item is treated. The IOStream implementation makes arbitrary decisions (such as treating uint8_t as a char, not a byte number). This IS a flaw of the IOStream design, as they try to achieve the unachievable.

C++ does not allow to classify a type - the language does not have the facility. There is no such thing as is_number_type() or is_character_type() IOStream could use to make a reasonable automatic choice. Ignoring that and trying to get away with guessing IS a design flaw of a library.

Admitted, printf() would equally fail to work in a generic "ShowVector()" implementation. But that is no excuse for iostream behavior. But it is very likely that in printf() case, ShowVector() would be defined like this:

template <class _T>
void ShowVector( const char *formatString, const char *title, const std::vector<_T> &v );
miken32
  • 42,008
  • 16
  • 111
  • 154
BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • 6
    The blame does not (purely) lie with iostream. Check what your `uint8_t` is a *typedef* for. Is it actually a char? Then don't blame iostreams for treating it like a char. – Martin Ba Dec 04 '13 at 22:27
  • And if you want to make sure that you get a number in generic code, you can use the [`num_put` facet](http://en.cppreference.com/w/cpp/locale/num_put) instead of the stream insertion operator. – Martin Ba Dec 04 '13 at 22:30
  • @Martin Ba You are right - c/c++ standards keep it open how many bytes a "short unsigned int" has. "unsigned char" is an idiosyncrasy of the language. If you really want a byte, you have to use an unsigned char. C++ also does not allow to impose restrictions on template arguments - such as "only numbers" and so if I changed the implementation of ShowVector to your proposed num_put solution, ShowVector could not display a vector of strings anymore, right? ;) – BitTickler Mar 19 '14 at 06:47
  • 1
    @Martin Bla: cppreference mentions that int8_t is a signed integer type with width of exactly 8 bits.I agree with author that it is strange that you get garbage output then, although it is technically explainable by the typedef and overload of char types in iostream. It could have been solved by having a __int8 a true type instead of a typedef. – gast128 Aug 18 '15 at 08:19
  • Oh, it's actually pretty easy to fix: // Fixes for std::ostream which has broken support for unsigned/signed/char types // and prints 8-bit integers like they were characters. namespace ostream_fixes { inline std::ostream& operator<< (std::ostream& os, unsigned char i) { return os << static_cast (i); } inline std::ostream& operator<< (std::ostream& os, signed char i) { return os << static_cast (i); } } // namespace ostream_fixes – mcv Oct 05 '17 at 11:40