default behaviour for std::streams with unsigned

-2

I think that when using ">>" "<<" stream operators, unsigned integer types are serialized/deserialized as binary data, while signed integer types are serialized as formatted text.

My assumption is:

uint32_t a= 17;
int b= 54321;
file_out<<a;   //write 0x00000011 to the file according to some endiannes
file_out<<b;   //write following ASCII characters '5''4''3''2''1' to the file.

Is my assumption explicitly stated in some standard? If yes, wich one of the following is faster?

file_out<<a;

or

file_out.write((char*)&a,sizeof(uint32_t);

compiler and libraries MinGW.

In particular is a bit strange that the same stream type offers different behaviours depending on input types.

c++
serialization
stream
asked on Stack Overflow Jun 2, 2013 by CoffeDeveloper • edited Jun 2, 2013 by CoffeDeveloper

1 Answer

2

Either your copy of the C++ Standard Library is broken or you are doing something that you have failed to tell us about. To be brief the default expected behavior is to output integral values in decimal format regardless of the types sign. The types sign is only taken into account to ensure that an unsigned value is not formatted as a signed value when the output format is set to decimal.

That said let's take a trip into Standard Land...

The key piece of information that you are interested in is how formatting is applied to unsigned integers. I'm going to try and highlight the important sections of the Standard that apply to output formatting (in regard to your question) and how integers are handled. Start by looking at how operator<< works when dealing with integer types. That is defined in $27.7.3.6.2.

From $27.7.3.6.2/1 - Arithmetic inserters [ostream.inserters.arithmetic]

operator<<(bool val);
operator<<(short val);
operator<<(unsigned short val);
operator<<(int val);
operator<<(unsigned int val);
operator<<(long val);
operator<<(unsigned long val);
operator<<(long long val);
operator<<(unsigned long long val);
operator<<(float val);
operator<<(double val);
operator<<(long double val);
operator<<(const void* val);

1 Effects: The classes num_get<> and num_put<> handle locale-dependent numeric formatting and parsing.

num_put is actually responsible for handling the formatting of integer values.

From $22.4.2.2.2/5 num_put virtual functions [facet.num.put.virtuals]

Stage 1: The first action of stage 1 is to determine a conversion specifier. The tables that describe this determination use the following local variables

fmtflags flags = str.flags() ;
fmtflags basefield = (flags & (ios_base::basefield));
fmtflags uppercase = (flags & (ios_base::uppercase));
fmtflags floatfield = (flags & (ios_base::floatfield));
fmtflags showpos = (flags & (ios_base::showpos));
fmtflags showbase = (flags & (ios_base::showbase));

All tables used in describing stage 1 are ordered. That is, the first line whose condition is true applies. A line without a condition is the default behavior when none of the earlier lines apply. For conversion from an integral type other than a character type, the function determines the integral conversion specifier as indicated in Table 87.

          Table 87 — Integer conversions  
State                                stdio equivalent
basefield == ios_base::oct           %o  
(basefield == ios_base::hex) && !uppercase %x  
(basefield == ios_base::hex)         %X  
for a signed integral type           %d  
for an unsigned integral type        %u  

There are two key pieces of information in the above except. The basefield which indicates the numerical formatting and that the determination is ordered. The basefield is important because it determines whether the value is output as hex, decimal, octal, etc. Streams are required to format integral values based on the format flags. Note that in order for the value to be formatted in hex the ios_base::hex flag must be set.

The format flags originate from ios_base.

From $27.5.3/2 Class ios_base [ios.base]

  1. It maintains several kinds of data:
    — control information that influences how to interpret (format) input sequences and how to generate (format) output sequences;

Another important aspect that needs to be taken into account is what the default flag values are when a stream is initialized. These are listed in a different table. Specifically table 128 which describes the effect calling basic_ios::init() has on a stream. For reference std::basic_ios inherits from std::ios_base. std::basic_ios is a base class of std::basic_ostream which is used to provide the specialized stream std::ostream, among others.

From $27.5.5.3 Table 128 - basic_ios::init() effects

Elements    Flags  
-----------------------------
flags()     skipws | dec

This means that after init() has been called a call to flags() should always return a value the same as (skipws | dec).

Put this all together and it means a freshly initialized stream is required to format integer values in decimal regardless of the types sign.

[The key pieces appear to be identical between C++03 and C++11]

answered on Stack Overflow Jun 3, 2013 by Captain Obvlious

User contributions licensed under CC BY-SA 3.0