Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Array Size

952 views
Skip to first unread message

Mike Copeland

unread,
Jun 19, 2013, 7:30:43 PM6/19/13
to
Is there a way to reference the number of elements (currently 4, of
course) in the declared array below? I wish to iterate through the
values in this array, but if I add to or delete values I want to
maintain my processing without changing other code.

std::string entrantTypes[] = {"a - Age Group", "e - Elite/Pro",
"t - Team", "m - Military"};

Please advise. TIA

Ian Collins

unread,
Jun 19, 2013, 7:38:11 PM6/19/13
to
Use a container (such as std::array or std::vector) rather than a naked
array.

--
Ian Collins

Juha Nieminen

unread,
Jun 20, 2013, 3:57:05 AM6/20/13
to
That really didn't answer the question, did it?

There is a way to resolve the amount of elements in an array, and it's
by doing the trick of

unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Ian Collins

unread,
Jun 20, 2013, 4:09:04 AM6/20/13
to
Assuming the definition of the array is visible to the compiler. Which
is why I suggested using a container which holds the array size.

--
Ian Collins

James Kanze

unread,
Jun 20, 2013, 9:35:19 AM6/20/13
to
Which begs the question. Usually, in my code, this will be
a `char const* const entrantTypes[] = ...`, to ensure static
initialization (and avoid order of initialization issues).
Using a container wouldn't work.

--
James

James Kanze

unread,
Jun 20, 2013, 9:38:26 AM6/20/13
to
The usual solution is to use the size function in your toolkit:

template <typename T, size_t n>
size_t
size( T (&array)[n] )
{
return n;
}

(This is C++03. In C++11, you'd probably want to make it
constexpr.)

Most of the time, of course, you'd just use the iterators
returned by (the similarly written) begin() and end() functions
(or if you have C++11, std::begin() and std::end()), and not
worry about the size.

Martin Shobe

unread,
Jun 20, 2013, 11:03:05 AM6/20/13
to
std::array is aggregate initialized, so, as far as initialization is
concerned, it will work wherever the c-style array would.

Martin Shobe

Luca Risolia

unread,
Jun 20, 2013, 12:35:21 PM6/20/13
to
Mike Copeland wrote:

> Is there a way to reference the number of elements (currently 4, of
> course) in the declared array below?

> std::string entrantTypes[] = {"a - Age Group", "e - Elite/Pro",
> "t - Team", "m - Military"};

// Valid for arrays
auto n = std::extent<decltype(entrantTypes)>::value;

// Valid for arrays and standard containers, O(1) with Random Access Iterators
auto n = std::distance(std::begin(entrantTypes), std::end(entrantTypes));

Ian Collins

unread,
Jun 20, 2013, 3:06:36 PM6/20/13
to
As is std::vector:

#include <vector>
#include <string>

int main()
{
std::vector<std::string> entrantTypes = {
"a - Age Group", "e - Elite/Pro",
"t - Team", "m - Military" };
}

--
Ian Collins

Martin Shobe

unread,
Jun 20, 2013, 3:46:30 PM6/20/13
to
No. That's an initializer_list passed to a constructor.

Martin Shobe

Luca Risolia

unread,
Jun 20, 2013, 3:52:58 PM6/20/13
to
Juha Nieminen wrote:

> There is a way to resolve the amount of elements in an array, and it's
> by doing the trick of
>
> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

As a general rule, it's safer and more portable to declare "amount" as
std::size_t:

std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);


Tobias Müller

unread,
Jun 20, 2013, 4:25:44 PM6/20/13
to
Juha Nieminen <nos...@thanks.invalid> wrote:
> There is a way to resolve the amount of elements in an array, and it's
> by doing the trick of
>
> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

I see this often as a macro:
#define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))

This has two major disadvantages:
1. The argument a is evaluated twice
2. It's not typesafe:
If a is not an array, but still has an operator [] defined, it gives the
wrong result.
Since arrays are often represented as pointer to the first element, this
can lead to subtle bugs.

Better is the following implementation (already mentioned by someone else
in this thread):
template <typename T, size_t n>
size_t array_size(T(&array)[n])
{
return n;
}

Tobi

Ike Naar

unread,
Jun 20, 2013, 4:56:20 PM6/20/13
to
On 2013-06-20, Tobias M?ller <tro...@bluewin.ch> wrote:
> Juha Nieminen <nos...@thanks.invalid> wrote:
>> There is a way to resolve the amount of elements in an array, and it's
>> by doing the trick of
>>
>> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
> I see this often as a macro:
> #define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))
>
> This has two major disadvantages:
> 1. The argument a is evaluated twice

Is it?
I thought the operand of sizeof is not evaluated.

guozhuc

unread,
Jun 20, 2013, 9:47:51 PM6/20/13
to
在 2013年6月21日星期五UTC+8上午4时56分20秒,Ike Naar写道:
I am also doubt, since sizeof is an compile-time operator, it may read the relative information of the operand but not evaluate it in my opinion, can someone explain ?

Ian Collins

unread,
Jun 21, 2013, 4:17:59 AM6/21/13
to
It isn't.

--
Ian Collins

Ian Collins

unread,
Jun 21, 2013, 4:18:44 AM6/21/13
to
Ah, good point!

--
Ian Collins

James Kanze

unread,
Jun 21, 2013, 10:49:12 AM6/21/13
to
Good programming practice would suggest int; it's better to
avoid the unsigned types except in special cases, and int is the
default type for integral values, unless there is a very strong
reason to do otherwise.

In some cases, the motivation for using something else is that
the results might not fit on an int. In this case, however...
The only time you'd be doing something like this is because
you've let the compiler count the initializers. And I defy you
to write a program with more than INT_MAX initializers:-). (It
could happen in machine generated code, of course, but if you're
not cross compiling, it probably won't compile, due to lack of
resources. And if it's machine generated code, there's no
problem generating a variable with the size at the same time.)

--
James

James Kanze

unread,
Jun 21, 2013, 10:53:37 AM6/21/13
to
On Thursday, 20 June 2013 16:03:05 UTC+1, Martin Shobe wrote:
> On 6/20/2013 8:35 AM, James Kanze wrote:
> > On Thursday, 20 June 2013 00:38:11 UTC+1, Ian Collins wrote:
> >> Mike Copeland wrote:

> >>> Is there a way to reference the number of elements (currently 4, of
> >>> course) in the declared array below? I wish to iterate through the
> >>> values in this array, but if I add to or delete values I want to
> >>> maintain my processing without changing other code.

> >>> std::string entrantTypes[] = {"a - Age Group", "e - Elite/Pro",
> >>> "t - Team", "m - Military"};

> >> Use a container (such as std::array or std::vector) rather than a naked
> >> array.

> > Which begs the question. Usually, in my code, this will be
> > a `char const* const entrantTypes[] = ...`, to ensure static
> > initialization (and avoid order of initialization issues).
> > Using a container wouldn't work.

> std::array is aggregate initialized, so, as far as initialization is
> concerned, it will work wherever the c-style array would.

Really? You'll notice that I didn't provide the dimension;
I let the compiler count the initializers, and figure it out
itself. std::array doesn't do that.

--
James

James Kanze

unread,
Jun 21, 2013, 11:05:36 AM6/21/13
to
I don't think that's aggregate initialization---std::vector
isn't an aggregate (std::array is).

In the context of the discussion, of course, the "aggregate
initialization" is because it is one of the requirements that an
initialization be purely static. (std::array meets all of those
requirements.) In particular, given:

std::array<char const*, 4> const entrantTypes =
{
"a - Age Group",
"e - Elite/Pro",
"t - Team",
"m - Military",
};

at namespace scope, I am guaranteed that it can be accessed from
any static constructor in any translation unit, without worrying
about order of initializer concerns. Using std::vector, or
std::array<std::string>, removes this guarantee.

On the other hand, using std::array still means that I have to
count the initializers by hand. (And it also means that I need
to have C++11. But before that, there was boost::array or
tr1::array.)

--
James

Scott Lurndal

unread,
Jun 21, 2013, 11:45:44 AM6/21/13
to
James Kanze <james...@gmail.com> writes:
>On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
>> Juha Nieminen wrote:
>> > There is a way to resolve the amount of elements in an array, and it's
>> > by doing the trick of
>
>> > unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>> As a general rule, it's safer and more portable to declare "amount" as
>> std::size_t:
>
>> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>Good programming practice would suggest int; it's better to
>avoid the unsigned types except in special cases, and int is the
>default type for integral values, unless there is a very strong
>reason to do otherwise.

Well, since sizeof() has a range that exceeded the range of 'int',
that would be a particularly stupid programming practice. The
correct datatype for sizeof() is, of course, size_t.

And I disagree with the 'avoid unsigned types'. Use the type that's
appropriate for the problem. The vast majority of types I use in
my current projects are unsigned (uint32_t, uint64_t).

scott

Öö Tiib

unread,
Jun 21, 2013, 1:45:26 PM6/21/13
to
On Friday, 21 June 2013 18:45:44 UTC+3, Scott Lurndal wrote:
> James Kanze <james...@gmail.com> writes:
> >On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
> >> As a general rule, it's safer and more portable to declare "amount" as
> >> std::size_t:
> >
> >> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
> >
> >Good programming practice would suggest int; it's better to
> >avoid the unsigned types except in special cases, and int is the
> >default type for integral values, unless there is a very strong
> >reason to do otherwise.
>
> Well, since sizeof() has a range that exceeded the range of 'int',
> that would be a particularly stupid programming practice. The
> correct datatype for sizeof() is, of course, size_t.

Whatever it can be called but it can't be called stupid!
Implementations do use quickest integer type on platform as 'int'.
So it is most efficient to use 'int' as type of integer value when
the value is anticipated to fit into range of 'int'. Such are quite
often all integer values in a program.

When a value does not fit into range of 'int' but fits into range
of 'unsigned int' then it makes more sense to take 'long' or
'long long' instead of 'unsigned int' because requirements for
range of that value tend to grow over range of 'unsigned int' in a
year or two anyway.

> And I disagree with the 'avoid unsigned types'. Use the type that's
> appropriate for the problem. The vast majority of types I use in
> my current projects are unsigned (uint32_t, uint64_t).

These types are good for bit-wise operations. What problem it is for
what you need integers in range 0 to 18446744073709551617?

Martin Shobe

unread,
Jun 21, 2013, 2:12:16 PM6/21/13
to
Really. I did notice that you didn't provide the dimension, but that's a
matter of type-deduction and not initialization. I would consider it a
valid reason to use a c-Style array instead of std::array.

Martin Shobe

Scott Lurndal

unread,
Jun 21, 2013, 2:38:25 PM6/21/13
to
new processor simulation. The internal busses are 64-bits and unsigned.

I've seen far to many cases in OS, hypervisor and simulation where int is
used for fundamentally unsigned data and bad things happen. And yes, I've
written all three of these in C++, several times.

In any case, uintX_t/intX_t aren't just good for 'bit-wise operations'.

additionally, there is no speed differential between uint32_t and int
on any modern platform (and no speed differential between int and uint64_t
on any 64-bit native platform; albeit there may be a minor space penalty
for uint64_t)

James suggestion would break for:

unsigned char bigguy[3u*1024u*1024u*1024u];

int bigguy_elements = sizeof(bigguy)/sizeof(bigguy[0]);

on any architecture where sizeof(size_t) != sizeof(int).

scott

Leigh Johnston

unread,
Jun 21, 2013, 4:48:26 PM6/21/13
to
On 21/06/2013 15:49, James Kanze wrote:
> On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
>> Juha Nieminen wrote:
>>> There is a way to resolve the amount of elements in an array, and it's
>>> by doing the trick of
>
>>> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>> As a general rule, it's safer and more portable to declare "amount" as
>> std::size_t:
>
>> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
> Good programming practice would suggest int; it's better to
> avoid the unsigned types except in special cases, and int is the
> default type for integral values, unless there is a very strong
> reason to do otherwise.

Utter nonsense; the correct type is size_t:

5.3.3/6
"The result of sizeof and sizeof... is a constant of type std::size_t."

>
> In some cases, the motivation for using something else is that
> the results might not fit on an int. In this case, however...
> The only time you'd be doing something like this is because
> you've let the compiler count the initializers. And I defy you
> to write a program with more than INT_MAX initializers:-). (It
> could happen in machine generated code, of course, but if you're
> not cross compiling, it probably won't compile, due to lack of
> resources. And if it's machine generated code, there's no
> problem generating a variable with the size at the same time.)

You could argue that 'int' should always be avoided and the new sized
typedefs (e.g. int32_t) used instead to ensure safe, portable code. The
MISRA C++ Coding Standard mandates use of such typedefs and avoidance of
'int', 'long' etc; the goal of MISRA coding standards is to provide
"safe, reliable software".

Motivations for using unsigned types include:
1) To represent values that are never negative, you cannot have a
negative size or a negative length or a negative count;
2) Bit twiddling.

/Leigh

Ian Collins

unread,
Jun 21, 2013, 5:03:13 PM6/21/13
to
Leigh Johnston wrote:
> On 21/06/2013 15:49, James Kanze wrote:
>> On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
>>> Juha Nieminen wrote:
>>>> There is a way to resolve the amount of elements in an array, and it's
>>>> by doing the trick of
>>
>>>> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>>
>>> As a general rule, it's safer and more portable to declare "amount" as
>>> std::size_t:
>>
>>> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>>
>> Good programming practice would suggest int; it's better to
>> avoid the unsigned types except in special cases, and int is the
>> default type for integral values, unless there is a very strong
>> reason to do otherwise.
>
> Utter nonsense; the correct type is size_t:
>
> 5.3.3/6
> "The result of sizeof and sizeof... is a constant of type std::size_t."

Or you could let the compiler make the appropriate choice:

auto amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

--
Ian Collins

Luca Risolia

unread,
Jun 21, 2013, 6:16:10 PM6/21/13
to
Ian Collins wrote:
>> 5.3.3/6
>> "The result of sizeof and sizeof... is a constant of type std::size_t."
>
> Or you could let the compiler make the appropriate choice:
>
> auto amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

Yes. The only possible choice is std::size_t by standard:

static_assert(std::is_same<decltype(amount), std::size_t>::value, "this
should never happen");

Öö Tiib

unread,
Jun 22, 2013, 4:56:17 AM6/22/13
to
On Friday, 21 June 2013 21:38:25 UTC+3, Scott Lurndal wrote:
> =?ISO-8859-1?Q?=D6=F6_Tiib?= <oot...@hot.ee> writes:
> >On Friday, 21 June 2013 18:45:44 UTC+3, Scott Lurndal wrote:
>
> >> And I disagree with the 'avoid unsigned types'. Use the type that's
> >> appropriate for the problem. The vast majority of types I use in
> >> my current projects are unsigned (uint32_t, uint64_t).
> >
> >These types are good for bit-wise operations. What problem it is for
> >what you need integers in range 0 to 18446744073709551617?
>
> new processor simulation. The internal busses are 64-bits and unsigned.

Yes. There you simply need them by requirements. That is strong
reason but lot of programs do not have such reasons.

> I've seen far to many cases in OS, hypervisor and simulation where int is
> used for fundamentally unsigned data and bad things happen. And yes, I've
> written all three of these in C++, several times.

I have been on the receiving end of pipe of such broken OS or processor.
Bad things happen because we humans do mistakes and do not test
properly, Both signed and unsigned go eventually out of limits and "two times
larger range" is red herring on most cases.

> In any case, uintX_t/intX_t aren't just good for 'bit-wise operations'.

Certainly there are some other special cases.

> additionally, there is no speed differential between uint32_t and int
> on any modern platform (and no speed differential between int and uint64_t
> on any 64-bit native platform; albeit there may be a minor space penalty
> for uint64_t)

It was not so long ago when It last mattered in my analyze ... it was unsigned
to floating point conversion that was lot slower than signed to floating
point conversion. At end the value vas anyway re-factored to floating point
to get rid of whatever conversion there.

> James suggestion would break for:
>
> unsigned char bigguy[3u*1024u*1024u*1024u];
>
> int bigguy_elements = sizeof(bigguy)/sizeof(bigguy[0]);
>
> on any architecture where sizeof(size_t) != sizeof(int).

Sure, but notice that the real problem might be that we use magic
numbers here. Lets try to rewrite it without magic numbers:

constexpr int bigguy_elements = 3000000000;
unsigned char bigguy[bigguy_elements];

We will get diagnostics. Usually several. However that may be
red herring and the actual defect may be (and often is) the very
need for such a large continuous pile of unsigned octets all at
once.

Gareth Owen

unread,
Jun 22, 2013, 5:05:38 AM6/22/13
to
sc...@slp53.sl.home (Scott Lurndal) writes:

> Well, since sizeof() has a range that exceeded the range of 'int',
> that would be a particularly stupid programming practice. The
> correct datatype for sizeof() is, of course, size_t.

Can we fit more than INT_MAX angels on the head of a pin?

/*
* aww, jeez, not this fricking argument again.
*
* both uses have strengths a weaknesses -
* can hold more values vs. can cause unintended promotions when
* compared to signed values and make other bugs more likely e.g.
*
* while(item_count >= 0) {
* do_something(item_count);
* --item_count; // Oops, if item_count is unsigned
* }
*
* Use the one that matters in your use case, comment your choice, and
* get on with doing actually useful programming
*/

James Kanze

unread,
Jun 22, 2013, 7:05:53 AM6/22/13
to
On Friday, June 21, 2013 4:45:44 PM UTC+1, Scott Lurndal wrote:
> James Kanze <james...@gmail.com> writes:
> >On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
> >> Juha Nieminen wrote:
> >> > There is a way to resolve the amount of elements in an array, and it's
> >> > by doing the trick of

> >> > unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

> >> As a general rule, it's safer and more portable to declare "amount" as
> >> std::size_t:

> >> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

> >Good programming practice would suggest int; it's better to
> >avoid the unsigned types except in special cases, and int is the
> >default type for integral values, unless there is a very strong
> >reason to do otherwise.

> Well, since sizeof() has a range that exceeded the range of 'int',
> that would be a particularly stupid programming practice.

Look at the context. Do you think it is possible for sizeof to
exceed the range of int in this case? Is it reasonably possible
that someone write more than 32000 initializers? Be serious.

> The
> correct datatype for sizeof() is, of course, size_t.
> And I disagree with the 'avoid unsigned types'.

The standard requires sizeof() to return a size_t. That doesn't
mean that you can't convert it to a more reasonable type.

> Use the type that's
> appropriate for the problem.

Which probably means: no built in types. It's an open question
as to whether making everything a class is worth the effort,
however. It's certainly safer, and the corresponding types are
more appropriate, but in many cases, it's a lot of effort for
only a little gain.

> The vast majority of types I use in
> my current projects are unsigned (uint32_t, uint64_t).

In other words, types that aren't even available in all
implementations. These types are only appropriate in very
limited cases.

--
James

James Kanze

unread,
Jun 22, 2013, 7:22:49 AM6/22/13
to
On Friday, June 21, 2013 7:38:25 PM UTC+1, Scott Lurndal wrote:
> =?ISO-8859-1?Q?=D6=F6_Tiib?= <oot...@hot.ee> writes:
> >On Friday, 21 June 2013 18:45:44 UTC+3, Scott Lurndal wrote:

> >> And I disagree with the 'avoid unsigned types'. Use the type that's
> >> appropriate for the problem. The vast majority of types I use in
> >> my current projects are unsigned (uint32_t, uint64_t).

> >These types are good for bit-wise operations. What problem it is for
> >what you need integers in range 0 to 18446744073709551617?

> new processor simulation. The internal busses are 64-bits and unsigned.

That's a very special case. You're emulating an entity which
consists of 64 individual bits. It's not an arithmetic value,
at least not in the traditional sense.

> I've seen far to many cases in OS, hypervisor and simulation where int is
> used for fundamentally unsigned data and bad things happen.

And I've seen far too many cases in normal applications where
the strange behavior of unsigned values causes problems.

The most widespread special case where you *do* use unsigned
types is when the code is manipulating bits, rather than
treating the value as an arithmetic type. If the usual (or
likely) binary operators are + and -, then you should not use
unsigned. If the binary operators are &, |, << and >>, you
should avoid signed.

For most applications, the second case simply doesn't occur (or
if it does, it in in "bitfield" types, implemented over an
enum). For simulating hardward, of course, this second case is
likely the most frequent.

> And yes, I've
> written all three of these in C++, several times.

> In any case, uintX_t/intX_t aren't just good for 'bit-wise operations'.

You certainly wouldn't want to use them for general numeric
values.

> additionally, there is no speed differential between uint32_t and int
> on any modern platform (and no speed differential between int and uint64_t
> on any 64-bit native platform; albeit there may be a minor space penalty
> for uint64_t)

> James suggestion would break for:

> unsigned char bigguy[3u*1024u*1024u*1024u];

> int bigguy_elements = sizeof(bigguy)/sizeof(bigguy[0]);

> on any architecture where sizeof(size_t) != sizeof(int).

Except that I never suggested anything like that. I clearly
said that for things like that, I would use:

std::vector<unsigned char> bigguy(...);

The *only* time you need to calculate the length is when it was
established by the compiler, from an initialization list. And
an initialization list which contains more than INT_MAX elements
almost certainly won't compile. (I had a case once, in machine
generated code, where the initialization list did get very
large. Much less than INT_MAX, but g+++ still aborted with out
of memory,)

I might also add that vectors of this size simple can't occur
in most applications, except perhaps in some very special,
limited cases. In fact, I've never seen a vector<unsigned char>
in my applications (although I can easily imagine other types of
applications where it makes sense); the only time I've had to
deal with memory blocks of that size was in the implementations
of memory management schemes: the blocks were based on pointers
I got back from sbrk(), *and* of course, I couldn't use size_t,
because the size of the blocks could be larger than would fit in
a size_t.

--
James

James Kanze

unread,
Jun 22, 2013, 7:35:20 AM6/22/13
to
On Friday, June 21, 2013 9:48:26 PM UTC+1, Leigh Johnston wrote:
> On 21/06/2013 15:49, James Kanze wrote:
> > On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
> >> Juha Nieminen wrote:
> >>> There is a way to resolve the amount of elements in an array, and it's
> >>> by doing the trick of

> >>> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

> >> As a general rule, it's safer and more portable to declare "amount" as
> >> std::size_t:

> >> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

> > Good programming practice would suggest int; it's better to
> > avoid the unsigned types except in special cases, and int is the
> > default type for integral values, unless there is a very strong
> > reason to do otherwise.

> Utter nonsense; the correct type is size_t:

> 5.3.3/6
> "The result of sizeof and sizeof... is a constant of type std::size_t."

The results of sizeof have nothing to do with the type you
should use. In my current application, all of my original data
is a string, but I tend to convert them to double when I have to
do arithmetic on them.

> > In some cases, the motivation for using something else is that
> > the results might not fit on an int. In this case, however...
> > The only time you'd be doing something like this is because
> > you've let the compiler count the initializers. And I defy you
> > to write a program with more than INT_MAX initializers:-). (It
> > could happen in machine generated code, of course, but if you're
> > not cross compiling, it probably won't compile, due to lack of
> > resources. And if it's machine generated code, there's no
> > problem generating a variable with the size at the same time.)

> You could argue that 'int' should always be avoided and the new sized
> typedefs (e.g. int32_t) used instead to ensure safe, portable code. The
> MISRA C++ Coding Standard mandates use of such typedefs and avoidance of
> 'int', 'long' etc; the goal of MISRA coding standards is to provide
> "safe, reliable software".

That's a good way to guarantee non-portable code.

> Motivations for using unsigned types include:
> 1) To represent values that are never negative, you cannot have a
> negative size or a negative length or a negative count;

In sum, you're pretending that the unsigned types are a subrange
type. If you had a case where the desired subrange was exactly
0...UINT_MAX *and* you needed modulo arithmetic over the
subrange, then unsigned types are the answer. (In fact, I use
them when calculating hash codes, exactly for this reason.)

With regards to sizes, lengths, and counts: it makes sense to
take the difference between values of these types. The
definition of the difference is abs(a - b). If this expression
doesn't give the correct results with a given implementation
type, then it is very bad engineering practice to use that
implementation type.

> 2) Bit twiddling.

Quite agreed there. This is, in fact, by far the most frequent
reason for using unsigned types, to the point where the presence
of an unsigned type is read as a flag that bit twiddling will
occur. (Although it depends on why I'm bit twiddling. If it's
something like manipulating the exponent and mantissa in a
double, or constructing or interpreting the bytes in a binary
protocol, various unsigned types are the way to go. If it's for
implementing a bitfield type, like ios_base::fmtflags, then I'll
usually use an enum, with overloaded operators.)

--
James

James Kanze

unread,
Jun 22, 2013, 7:39:46 AM6/22/13
to
On Friday, June 21, 2013 10:03:13 PM UTC+1, Ian Collins wrote:

[...]
> Or you could let the compiler make the appropriate choice:

> auto amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

That's a dangerous path to go down, since you end up not knowing
exactly what you've got. In this case, it's clearly size_t (so
you know that you need to convert it to a signed type for most
uses). In more complicated cases, you may not even know whether
it is signed or not (which is the worst case to be in).

There are some (maybe even a lot) of valid uses of auto, but
just using it everywhere because you're too lazy to work out the
type is definitely not good engineering practice.

--
James

James Kanze

unread,
Jun 22, 2013, 7:41:46 AM6/22/13
to
Exactly. And it's *not* the type you want, since it's extremely
error prone.

--
James

Jorgen Grahn

unread,
Jun 22, 2013, 8:44:55 AM6/22/13
to
On Sat, 2013-06-22, Gareth Owen wrote:
> sc...@slp53.sl.home (Scott Lurndal) writes:
>
>> Well, since sizeof() has a range that exceeded the range of 'int',
>> that would be a particularly stupid programming practice. The
>> correct datatype for sizeof() is, of course, size_t.
>
> Can we fit more than INT_MAX angels on the head of a pin?
>
> /*
> * aww, jeez, not this fricking argument again.

Yeah. Was it here or over in comp.lang.c, not too long ago? A really
long, heated argument about signed/unsigned ... and I don't think
anyone learned much from it.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Leigh Johnston

unread,
Jun 22, 2013, 12:44:53 PM6/22/13
to
On 22/06/2013 12:35, James Kanze wrote:
> On Friday, June 21, 2013 9:48:26 PM UTC+1, Leigh Johnston wrote:
>> On 21/06/2013 15:49, James Kanze wrote:
>>> On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
>>>> Juha Nieminen wrote:
>>>>> There is a way to resolve the amount of elements in an array, and it's
>>>>> by doing the trick of
>
>>>>> unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>>>> As a general rule, it's safer and more portable to declare "amount" as
>>>> std::size_t:
>
>>>> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>>> Good programming practice would suggest int; it's better to
>>> avoid the unsigned types except in special cases, and int is the
>>> default type for integral values, unless there is a very strong
>>> reason to do otherwise.
>
>> Utter nonsense; the correct type is size_t:
>
>> 5.3.3/6
>> "The result of sizeof and sizeof... is a constant of type std::size_t."
>
> The results of sizeof have nothing to do with the type you
> should use. In my current application, all of my original data
> is a string, but I tend to convert them to double when I have to
> do arithmetic on them.

Wrong. The type of sizeof has everything to do with the type you should
use. Like it or not std::size_t is extremely pervasive in the standard
library so it makes sense to use std::size_t when interfacing with the
standard library. Take the standard containers: by default they all use
the allocator std::allocator which has a typedef "size_type" (which is a
std::size_t) and this unsigned type is then used in the interfaces of
those containers which perform indexing. std::vector<T>::operator[]
takes a std::size_t not an 'int' so provide it with a std::size_t not an
'int'.

>>> In some cases, the motivation for using something else is that
>>> the results might not fit on an int. In this case, however...
>>> The only time you'd be doing something like this is because
>>> you've let the compiler count the initializers. And I defy you
>>> to write a program with more than INT_MAX initializers:-). (It
>>> could happen in machine generated code, of course, but if you're
>>> not cross compiling, it probably won't compile, due to lack of
>>> resources. And if it's machine generated code, there's no
>>> problem generating a variable with the size at the same time.)
>
>> You could argue that 'int' should always be avoided and the new sized
>> typedefs (e.g. int32_t) used instead to ensure safe, portable code. The
>> MISRA C++ Coding Standard mandates use of such typedefs and avoidance of
>> 'int', 'long' etc; the goal of MISRA coding standards is to provide
>> "safe, reliable software".
>
> That's a good way to guarantee non-portable code.

Wrong. The exact opposite is true: it's a good way to guarantee portable
code as 'int' can be different sizes on different platforms whilst
'int32_t' cannot. The only argument in favour of using 'int' is that
'int' might be the fastest type on that platform in which case I would
define the type 'fast_int_t' and only use it in places that are
performance bottlenecks (5% of your code base).

>
>> Motivations for using unsigned types include:
>> 1) To represent values that are never negative, you cannot have a
>> negative size or a negative length or a negative count;
>
> In sum, you're pretending that the unsigned types are a subrange
> type. If you had a case where the desired subrange was exactly
> 0...UINT_MAX *and* you needed modulo arithmetic over the
> subrange, then unsigned types are the answer. (In fact, I use
> them when calculating hash codes, exactly for this reason.)

Nonsense, if we follow that argument we should always use 'char' or
'short' if we are not utilizing the full range of 'int' which is of
course bollocks.

>
> With regards to sizes, lengths, and counts: it makes sense to
> take the difference between values of these types. The
> definition of the difference is abs(a - b). If this expression
> doesn't give the correct results with a given implementation
> type, then it is very bad engineering practice to use that
> implementation type.

Bad engineering practice my arse; you simply use a cast in the
appropriate place and if you forget to cast and are using abs then the
compiler will complain:

int main()
{
typedef unsigned int length_t;
typedef int difference_t;
length_t a = 42;
length_t b = 2;
std::cout << "42 - 2 = " << abs(static_cast<difference_t>(a - b)) <<
std::endl;
std::cout << "2 - 42 = " << abs(static_cast<difference_t>(b - a)) <<
std::endl;
}


42 - 2 = 40
2 - 42 = 40

/Leigh

Luca Risolia

unread,
Jun 22, 2013, 2:45:39 PM6/22/13
to
No. Implicit, narrowing conversions are error prone. Whenever you need
different types for some reasons, you should make it clear via explicit casts.

Juha Nieminen

unread,
Jun 24, 2013, 3:30:47 AM6/24/13
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> And I disagree with the 'avoid unsigned types'. Use the type that's
> appropriate for the problem. The vast majority of types I use in
> my current projects are unsigned (uint32_t, uint64_t).

That principle has bitten me more than once.

For example in the past I adhered strictly to the convention of
"if negative values make no sense for an integral type, use an unsigned
type instead." Thus, for example, I used unsigned types to denote the
dimensions of an image or the screen.

And then I found myself writing things like this:

double destX = x + screenWidth / 2;

and wondering why it's not working properly.

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Juha Nieminen

unread,
Jun 24, 2013, 3:33:31 AM6/24/13
to
Ian Collins <ian-...@hotmail.com> wrote:
>>> I see this often as a macro:
>>> #define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))
>>>
>>> This has two major disadvantages:
>>> 1. The argument a is evaluated twice
>>
>> Is it?
>> I thought the operand of sizeof is not evaluated.
>
> It isn't.

Technically speaking it is "evaluated twice". It's just that it's
evaluated twice at compile time, not at runtime. (Therefore it's
completely inconsequential in practice.)

Besides, the proper macro should have been

#define ARRAY_SIZE(a) (sizeof((a))/sizeof((a[0])))

Gerhard Fiedler

unread,
Jun 24, 2013, 8:56:52 AM6/24/13
to
(Presumably x is signed and screenWidth is unsigned.)

How about enabling warnings about mixing signed and unsigned integers in
an expression?

Gerhard

Gerhard Fiedler

unread,
Jun 24, 2013, 9:01:51 AM6/24/13
to
Juha Nieminen wrote:

> Ian Collins <ian-...@hotmail.com> wrote:
>>>> I see this often as a macro:
>>>> #define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))
>>>>
>>>> This has two major disadvantages:
>>>> 1. The argument a is evaluated twice
>>>
>>> Is it?
>>> I thought the operand of sizeof is not evaluated.
>>
>> It isn't.
>
> Technically speaking it is "evaluated twice". It's just that it's
> evaluated twice at compile time, not at runtime. (Therefore it's
> completely inconsequential in practice.)
>
> Besides, the proper macro should have been
>
> #define ARRAY_SIZE(a) (sizeof((a))/sizeof((a[0])))

Can someone please explain what the difference is between sizeof(a) and
sizeof((a)) in a macro? I know about the rule to always use macro
arguments in parentheses (to avoid operator precedence problems), but
isn't the argument of sizeof(a) already enclosed in parentheses?

Thanks,
Gerhard

Scott Lurndal

unread,
Jun 24, 2013, 9:45:54 AM6/24/13
to
No, I cannot. My compiler still treats the auto keyword as deprecated.

It will likely be close to a decade before C++11 will be widely
enough available (in real shops) such that it can be used in legacy
or even new projects.

Scott Lurndal

unread,
Jun 24, 2013, 9:49:00 AM6/24/13
to
James Kanze <james...@gmail.com> writes:
>On Friday, June 21, 2013 4:45:44 PM UTC+1, Scott Lurndal wrote:
>> James Kanze <james...@gmail.com> writes:
>> >On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
>> >> Juha Nieminen wrote:
>> >> > There is a way to resolve the amount of elements in an array, and it's
>> >> > by doing the trick of
>
>> >> > unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>> >> As a general rule, it's safer and more portable to declare "amount" as
>> >> std::size_t:
>
>> >> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);
>
>> >Good programming practice would suggest int; it's better to
>> >avoid the unsigned types except in special cases, and int is the
>> >default type for integral values, unless there is a very strong
>> >reason to do otherwise.
>
>> Well, since sizeof() has a range that exceeded the range of 'int',
>> that would be a particularly stupid programming practice.
>
>Look at the context. Do you think it is possible for sizeof to
>exceed the range of int in this case? Is it reasonably possible
>that someone write more than 32000 initializers? Be serious.

Yes. Software changes over time. A 3GB array of bytes is not
particularly uncommon in systems programming, particularly on
64-bit systems.

"int" should _NEVER EVER_ replace size_t in a 64-bit application. Period.

>
>> The
>> correct datatype for sizeof() is, of course, size_t.
>> And I disagree with the 'avoid unsigned types'.
>
>The standard requires sizeof() to return a size_t. That doesn't
>mean that you can't convert it to a more reasonable type.

Narrowing is almost never safe.

>
>> Use the type that's
>> appropriate for the problem.
>
>Which probably means: no built in types. It's an open question
>as to whether making everything a class is worth the effort,
>however. It's certainly safer, and the corresponding types are
>more appropriate, but in many cases, it's a lot of effort for
>only a little gain.
>
>> The vast majority of types I use in
>> my current projects are unsigned (uint32_t, uint64_t).
>
>In other words, types that aren't even available in all
>implementations. These types are only appropriate in very
>limited cases.

These are ANSI C (and std C++ types).

scott

Scott Lurndal

unread,
Jun 24, 2013, 9:50:25 AM6/24/13
to
I always set GCC up with -Wall and -Werror for this reason, amongst others.

scott

Ian Collins

unread,
Jun 24, 2013, 4:13:16 PM6/24/13
to
Mixing signed and unsigned types in expressions is always messy and
sometimes unavoidable. Unfortunately standards make it inevitable and
the only way to avoid a warning is a cast. Even a trivial function like
POSIX read requires mixing size_t and ssize_t, for example:

void f( int fd )
{
const size_t siz = 42;
char buf[siz];

ssize_t n = read( fd, buf, siz );

if( n != siz ) // Warning!
{
}
}

The "cleanest" approach is to use ssize_t throughout. I often wish
size_t was signed...

--
Ian Collins

Ian Collins

unread,
Jun 24, 2013, 4:16:37 PM6/24/13
to
Odd, I shipped my first "real" C++11 application last month. My other
client who uses C++ for embedded targets is also using gcc 4.6 or newer.

--
Ian Collins

Ian Collins

unread,
Jun 24, 2013, 4:21:13 PM6/24/13
to
I tend to agree in this case, especially as the argument to sizeof is a
type rather than a value.

--
Ian Collins

Jorgen Grahn

unread,
Jun 24, 2013, 4:46:57 PM6/24/13
to
On Mon, 2013-06-24, Scott Lurndal wrote:
...
> It will likely be close to a decade before C++11 will be widely
> enough available (in real shops) such that it can be used in legacy
> or even new projects.

Which area and kind of shop is this? I'm rather conservative, and
also pessimistic about large projects keeping their tools up to date
... but almost a decade seems like an awfully long time.

(I'm assuming you're just talking about the availability of tools, and
the willingness to integrate them into the project. Having people
learn to /use/ C++11 is a different, and probably worse, problem.)

woodb...@gmail.com

unread,
Jun 24, 2013, 5:20:19 PM6/24/13
to
As far as I can tell these types are becoming more widely
available. (I don't care if a platform can't implement all
of them.)

Brian
Ebenezer Enterprises
http://webEbenezer.net

Gareth Owen

unread,
Jun 25, 2013, 1:27:48 AM6/25/13
to
Leigh Johnston <le...@i42.co.uk> writes:

> Bad engineering practice my arse; you simply use a cast in the
> appropriate place [snip]

> std::cout << "42 - 2 = " << abs(static_cast<difference_t>(a -
> b)) << std::endl;
> std::cout << "2 - 42 = " << abs(static_cast<difference_t>(b -
> a)) << std::endl;

I think its safe to say we don't have the same understanding of the word
"simply". Personally, I consider arithmetic types on which I can do
arithmetic to be "simpler" than those which require a cast.

Tobias Müller

unread,
Jun 25, 2013, 2:07:03 AM6/25/13
to
Gerhard Fiedler <gel...@gmail.com> wrote:
> Juha Nieminen wrote:
>>>>> I see this often as a macro:
>>>>> #define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))
>> Besides, the proper macro should have been
>>
>> #define ARRAY_SIZE(a) (sizeof((a))/sizeof((a[0])))
>
> Can someone please explain what the difference is between sizeof(a) and
> sizeof((a)) in a macro? I know about the rule to always use macro
> arguments in parentheses (to avoid operator precedence problems), but
> isn't the argument of sizeof(a) already enclosed in parentheses?

I always use parenteses around all uses of macro parameters mechanically,
even if operator precedence is not a problem, even if it is used again as
argument to a function or macro.
Since sizeof is an operator and not a function, it is really superfluous
here. But I prefer using sizeof like a function.

The reason for me to use parenteses (even if the macro parameter is again
used as function argument) was always, that the expanded parameter could
contain the comma operator. But that's actually impossible.

But now I got used to it and if in doubt, it helps to avoid nasty errors.

Tobi

Juha Nieminen

unread,
Jun 25, 2013, 5:13:21 AM6/25/13
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> It will likely be close to a decade before C++11 will be widely
> enough available (in real shops) such that it can be used in legacy
> or even new projects.

Is there a widely used C++ compiler out there that does not support C++11?

Drew Lawson

unread,
Jun 25, 2013, 8:39:21 AM6/25/13
to
In article <slrnkshc20.2...@frailea.sa.invalid>
Jorgen Grahn <grahn...@snipabacken.se> writes:
>On Mon, 2013-06-24, Scott Lurndal wrote:
>...
>> It will likely be close to a decade before C++11 will be widely
>> enough available (in real shops) such that it can be used in legacy
>> or even new projects.
>
>Which area and kind of shop is this? I'm rather conservative, and
>also pessimistic about large projects keeping their tools up to date
>... but almost a decade seems like an awfully long time.

Contracted project staffing is one. I work on a large-ish legacy
system for a branch of the US DoD. The PC component is primarily
using Visual Studio 2003. We will soon be upgrading that portion
to Visual Studio 2010.

When production and spending are two entirely separate entities,
it is not uncommon to have the view, "If it ain't broke, why approve
funding to change it with no project benefit."

I used to work at the headquarters of a large US grocery chain. At
least in their case, the same perspective holds for many internal-use
corporate systems. ("Yes, it is crap. But it is your job to use
it, so stop whining.")

--
Drew Lawson | If dreams were thunder,
| and lightning was desire,
| This old house would have burnt down
| a long time ago

Scott Lurndal

unread,
Jun 25, 2013, 9:53:53 AM6/25/13
to
Jorgen Grahn <grahn...@snipabacken.se> writes:
>On Mon, 2013-06-24, Scott Lurndal wrote:
>...
>> It will likely be close to a decade before C++11 will be widely
>> enough available (in real shops) such that it can be used in legacy
>> or even new projects.
>
>Which area and kind of shop is this? I'm rather conservative, and
>also pessimistic about large projects keeping their tools up to date
>... but almost a decade seems like an awfully long time.
>
>(I'm assuming you're just talking about the availability of tools, and
>the willingness to integrate them into the project. Having people
>learn to /use/ C++11 is a different, and probably worse, problem.)

Inertia rules.

Shops are, for the most part, quite conservative. If something works,
don't break it. Changing compilers is not as simple as "yum update";
all the existing projects using the current compiler version need be
updated for the new (with -Wall/-Werror it is quite likely that code
that compiled with the current version won't compile with the next).

Said applications, once recompiled, need unit and production testing.
This all costs real money.

That doesn't even consider the use of third-party object code/libraries
which are often tied to particular compiler/{c|cpp}-library versions.

Heck, look at how many shops still use COBOL on zSeries!

In my CPOE, we are currently using gcc 4.1.2 (2008), and because of
third-party object dependencies, not likely to change in the near
future. Not to mention that there is little (except perhaps native threads)
in the C++11 standard that is compelling enough to drive a compiler
version upgrade. And since pthreads work just fine in our apps ...

scott

Scott Lurndal

unread,
Jun 25, 2013, 9:55:29 AM6/25/13
to
Juha Nieminen <nos...@thanks.invalid> writes:
>Scott Lurndal <sc...@slp53.sl.home> wrote:
>> It will likely be close to a decade before C++11 will be widely
>> enough available (in real shops) such that it can be used in legacy
>> or even new projects.
>
>Is there a widely used C++ compiler out there that does not support C++11?

g++.

Just because the most recent version has it, doesn't mean that the millions
of users of the existing and prior versions are going to instantly change
over. That costs money and it is completely unnecessary.

James Kanze

unread,
Jun 25, 2013, 11:51:33 AM6/25/13
to
On Monday, 24 June 2013 14:49:00 UTC+1, Scott Lurndal wrote:
> James Kanze <james...@gmail.com> writes:
> >On Friday, June 21, 2013 4:45:44 PM UTC+1, Scott Lurndal wrote:
> >> James Kanze <james...@gmail.com> writes:
> >> >On Thursday, 20 June 2013 20:52:58 UTC+1, Luca Risolia wrote:
> >> >> Juha Nieminen wrote:
> >> >> > There is a way to resolve the amount of elements in an array, and it's
> >> >> > by doing the trick of

> >> >> > unsigned amount = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

> >> >> As a general rule, it's safer and more portable to declare "amount" as
> >> >> std::size_t:

> >> >> std::size_t = sizeof(entrantTypes) / sizeof(entrantTypes[0]);

> >> >Good programming practice would suggest int; it's better to
> >> >avoid the unsigned types except in special cases, and int is the
> >> >default type for integral values, unless there is a very strong
> >> >reason to do otherwise.

> >> Well, since sizeof() has a range that exceeded the range of 'int',
> >> that would be a particularly stupid programming practice.

> >Look at the context. Do you think it is possible for sizeof to
> >exceed the range of int in this case? Is it reasonably possible
> >that someone write more than 32000 initializers? Be serious.

> Yes. Software changes over time. A 3GB array of bytes is not
> particularly uncommon in systems programming, particularly on
> 64-bit systems.

If the context is such that such a large array of bytes could
conceivable occur, use long. Or long long. Or ptrdiff_t.

You can also do a checked conversion; I use these a lot in
legacy code, where I don't know enough about the application
domain to be able to reasonably limit the size. But for
a manually written list of initializers... people being what
they are, no one is going to write out more than 2GB of
initializers, so if you're not concerned about portability to
smaller systems (and so you know that int is at least 32 bits),
checking or using some larger type is overkill.

> "int" should _NEVER EVER_ replace size_t in a 64-bit application. Period.

The "int" is not *replacing* anything. It's the desired type,
using the language as it was designed to be used.

> >> The
> >> correct datatype for sizeof() is, of course, size_t.
> >> And I disagree with the 'avoid unsigned types'.

> >The standard requires sizeof() to return a size_t. That doesn't
> >mean that you can't convert it to a more reasonable type.

> Narrowing is almost never safe.

Really? I can think of a lot of cases where it is safe.
Fundamentally, it's as safe as addition or multiplication (since
these can easily generate values which are larger than the
targeted type as well). And you treat it in the same way: you
validate your input, to be sure that it cannot create values
which will overflow.

> >> Use the type that's
> >> appropriate for the problem.

> >Which probably means: no built in types. It's an open question
> >as to whether making everything a class is worth the effort,
> >however. It's certainly safer, and the corresponding types are
> >more appropriate, but in many cases, it's a lot of effort for
> >only a little gain.

> >> The vast majority of types I use in
> >> my current projects are unsigned (uint32_t, uint64_t).

> >In other words, types that aren't even available in all
> >implementations. These types are only appropriate in very
> >limited cases.

> These are ANSI C (and std C++ types).

From the C standard (§7.18.1.1/3, concerning exact-width integer
types): "These types are optional." They are only required if
they correspond to one of the standard integral types. And
there are architectures where this is not the case.

--
James

James Kanze

unread,
Jun 25, 2013, 11:53:03 AM6/25/13
to
Some platforms can't implement any of them, or only "uint8_t".

--
James

James Kanze

unread,
Jun 25, 2013, 11:57:58 AM6/25/13
to
On Monday, 24 June 2013 21:46:57 UTC+1, Jorgen Grahn wrote:
> On Mon, 2013-06-24, Scott Lurndal wrote:
> ...
> > It will likely be close to a decade before C++11 will be widely
> > enough available (in real shops) such that it can be used in legacy
> > or even new projects.

> Which area and kind of shop is this? I'm rather conservative, and
> also pessimistic about large projects keeping their tools up to date
> ... but almost a decade seems like an awfully long time.

It depends on the type of project (and how close you have to be
for "almost"). On critical projects, vetting a compiler is
a major undertaking, and you won't upgrade until you've verified
that the new version has no bugs that affect the code you've
written (which carefully avoids the bugs in the older version).
And since vetting the new version is also an expensive
undertaking (in man/months), you won't do it until you think the
benefits will out-weigh the cost.

> (I'm assuming you're just talking about the availability of tools, and
> the willingness to integrate them into the project. Having people
> learn to /use/ C++11 is a different, and probably worse, problem.)

There's that, too. You have to update your coding guidelines as
well.

--
James

James Kanze

unread,
Jun 25, 2013, 12:04:48 PM6/25/13
to
On Monday, 24 June 2013 14:01:51 UTC+1, Gerhard Fiedler wrote:
> Juha Nieminen wrote:

[...]
> > Technically speaking it is "evaluated twice". It's just that it's
> > evaluated twice at compile time, not at runtime. (Therefore it's
> > completely inconsequential in practice.)

> > Besides, the proper macro should have been

> > #define ARRAY_SIZE(a) (sizeof((a))/sizeof((a[0])))

> Can someone please explain what the difference is between sizeof(a) and
> sizeof((a)) in a macro? I know about the rule to always use macro
> arguments in parentheses (to avoid operator precedence problems), but
> isn't the argument of sizeof(a) already enclosed in parentheses?

For that matter, the second term should be "sizeof((a)[0])" if
you're doing it "right".

Since the macro only gives the correct results if given the name
of an actual C style array, the extra internal parentheses may
be a bit of overkill anyway. The fact that ARRAY_SIZE( a + 1 )
fails to compile (because the expression sizeof(a + 1[0]) isn't
legal) could even be considered an advantage.

--
James

Scott Lurndal

unread,
Jun 25, 2013, 12:32:19 PM6/25/13
to
James Kanze <james...@gmail.com> writes:
>On Monday, 24 June 2013 14:49:00 UTC+1, Scott Lurndal wrote:

>> >> Well, since sizeof() has a range that exceeded the range of 'int',
>> >> that would be a particularly stupid programming practice.
>
>> >Look at the context. Do you think it is possible for sizeof to
>> >exceed the range of int in this case? Is it reasonably possible
>> >that someone write more than 32000 initializers? Be serious.
>
>> Yes. Software changes over time. A 3GB array of bytes is not
>> particularly uncommon in systems programming, particularly on
>> 64-bit systems.
>
>If the context is such that such a large array of bytes could
>conceivable occur, use long. Or long long. Or ptrdiff_t.

contexts change over time. 5 years later, someone changes the context
(by making an array larger, for example). Meanwhile, five source
files away, someone used "int" to represent the length of the array
and bam! bad things happen.

Your reasoning for using 'int' or even 'long' instead of the correct type
(size_t in this case, or if you really need signed, ssize_t) is unconvincing.

scott

(Yes, I know that theoretically, ssize_t only needs to support a single non-positive
value, but that's not the case on any relevent modern architecture).

David Brown

unread,
Jun 25, 2013, 1:02:06 PM6/25/13
to
Can you name a platform that supports standard C types (int of at least
16 bits, long int of at least 32-bit, and unsigned versions) that does
not support at least uint32_t and int32_t ? Preferably one that has
been used this century?

There are some DSP architectures that can't define 8-bit or even 16-bit
types, because the hardware doesn't support them. And there are plenty
of C compilers for 8-bit cpus that don't support 64-bit types.

I've known a few C compilers for very odd processors, such as
Freescale's eTPU timer unit, that have odd integer sizes (24-bit
integers for some generations of the eTPU). But working with these
sorts of systems is so odd and specialised that it is far removed from
"normal" C programming.

For almost everybody, and almost every compiler, you can use uint8_t,
int8_t, etc., up to at least 32-bit types.


woodb...@gmail.com

unread,
Jun 25, 2013, 2:57:53 PM6/25/13
to
If a company wants their platform to be able to interact with
other platforms the easiest way to do so is to support a subset
of the exact-width types. C++'s short/int are a headache from
a middleware perspective.

Luca Risolia

unread,
Jun 25, 2013, 2:59:06 PM6/25/13
to
James Kanze wrote:

>> >> The vast majority of types I use in
>> >> my current projects are unsigned (uint32_t, uint64_t).

>> These are ANSI C (and std C++ types).
>
> From the C standard (§7.18.1.1/3, concerning exact-width integer
> types): "These types are optional." They are only required if
> they correspond to one of the standard integral types. And
> there are architectures where this is not the case.

On the other hand, the minimum-width integer types uint_least8_t
uint_least16_t, uint_least32_t, uint_least64_t are guaranteed to exist in
every C++11-compliant implementation, which are what you really need most of
the times.

James Kanze

unread,
Jun 25, 2013, 3:35:12 PM6/25/13
to
On Tuesday, 25 June 2013 18:02:06 UTC+1, David Brown wrote:
> On 25/06/13 17:53, James Kanze wrote:
> > On Monday, 24 June 2013 22:20:19 UTC+1, woodb...@gmail.com wrote:
> >> On Saturday, June 22, 2013 11:05:53 AM UTC, James Kanze wrote:
> >>> On Friday, June 21, 2013 4:45:44 PM UTC+1, Scott Lurndal wrote:
> >>>> The vast majority of types I use in
> >>>> my current projects are unsigned (uint32_t, uint64_t).

> >>> In other words, types that aren't even available in all
> >>> implementations. These types are only appropriate in very
> >>> limited cases.

> >> As far as I can tell these types are becoming more widely
> >> available. (I don't care if a platform can't implement all
> >> of them.)

> > Some platforms can't implement any of them, or only "uint8_t".

> Can you name a platform that supports standard C types (int of at least
> 16 bits, long int of at least 32-bit, and unsigned versions) that does
> not support at least uint32_t and int32_t ? Preferably one that has
> been used this century?

The Unisys mainframes. And at least one of the architectures is
still being manufactured (and both are certainly still being
used, although they never achieved the numbers IBM did).

--
James

James Kanze

unread,
Jun 25, 2013, 3:39:27 PM6/25/13
to
On Tuesday, 25 June 2013 19:59:06 UTC+1, Luca Risolia wrote:
> James Kanze wrote:
> > From the C standard (§7.18.1.1/3, concerning exact-width integer
> > types): "These types are optional." They are only required if
> > they correspond to one of the standard integral types. And
> > there are architectures where this is not the case.

> On the other hand, the minimum-width integer types uint_least8_t
> uint_least16_t, uint_least32_t, uint_least64_t are guaranteed to exist in
> every C++11-compliant implementation, which are what you really need most of
> the times.

I've never needed them (although I can easily imagine cases
where one might). In practice, for *most* values, it's
preferable to work in the other direction: determine the valid
values of your input as a function of INT_MAX, and then validate
the input, so that you cannot have any sort of overflow later.

The fact that sizeof(x) or std::vector<T>::size() might not fit
in an int is a red herring. There are lots and lots of other
things that might not fit in an int as well. Everything you do,
however, is a function of your input, and you validate that up
front.

--
James

Jorgen Grahn

unread,
Jun 25, 2013, 3:57:02 PM6/25/13
to
On Tue, 2013-06-25, Scott Lurndal wrote:
> Jorgen Grahn <grahn...@snipabacken.se> writes:
>>On Mon, 2013-06-24, Scott Lurndal wrote:
>>...
>>> It will likely be close to a decade before C++11 will be widely
>>> enough available (in real shops) such that it can be used in legacy
>>> or even new projects.
>>
>>Which area and kind of shop is this? I'm rather conservative, and
>>also pessimistic about large projects keeping their tools up to date
>>... but almost a decade seems like an awfully long time.
...

> Inertia rules.
>
> Shops are, for the most part, quite conservative. If something works,
> don't break it. Changing compilers is not as simple as "yum update";
> all the existing projects using the current compiler version need be
> updated for the new

Yes, but there's the other side of the coin too. The farther between
the toolchain/OS upgrades, the more painful they become. And when
something finally forces you to do one, it's likely to come at the
worst possible time.

Not having a strategy for regular upgrades is a risk.

> (with -Wall/-Werror it is quite likely that code
> that compiled with the current version won't compile with the next).

I've never found that part to be more than an annoyance -- the fixes
tend to be obvious, backwards-compatible and review-only. Usually you
forgot an #include somewhere.

Scott Lurndal

unread,
Jun 25, 2013, 4:09:23 PM6/25/13
to
Jorgen Grahn <grahn...@snipabacken.se> writes:
>On Tue, 2013-06-25, Scott Lurndal wrote:
>> Jorgen Grahn <grahn...@snipabacken.se> writes:
>>>On Mon, 2013-06-24, Scott Lurndal wrote:
>>>...
>>>> It will likely be close to a decade before C++11 will be widely
>>>> enough available (in real shops) such that it can be used in legacy
>>>> or even new projects.
>>>
>>>Which area and kind of shop is this? I'm rather conservative, and
>>>also pessimistic about large projects keeping their tools up to date
>>>... but almost a decade seems like an awfully long time.
>...
>
>> Inertia rules.
>>
>> Shops are, for the most part, quite conservative. If something works,
>> don't break it. Changing compilers is not as simple as "yum update";
>> all the existing projects using the current compiler version need be
>> updated for the new
>
>Yes, but there's the other side of the coin too. The farther between
>the toolchain/OS upgrades, the more painful they become. And when
>something finally forces you to do one, it's likely to come at the
>worst possible time.

Decoupling an OS upgrade from a toolchain upgrade is straightforward
and common. And unlike Windows where frequent upgrades and
incompatibilities are introduced regularly, linux installations can
survive for a decade without significant disruption (assuming the hardware
lasts that long).

>
>Not having a strategy for regular upgrades is a risk.

Most places I've worked would time the upgrade of a toolchain
with the introduction of a new product, the existing products
will continue to use the older toolchain, with both generally
installed on the build systems. Another nice feature of linux;
just try to install VS2003 and VS2013 on the same system.

>
>> (with -Wall/-Werror it is quite likely that code
>> that compiled with the current version won't compile with the next).
>
>I've never found that part to be more than an annoyance -- the fixes
>tend to be obvious, backwards-compatible and review-only. Usually you
>forgot an #include somewhere.

Annoyance or not, any single checkin will restart the QA process. Which
takes time and costs money. At my PPOE, the QA process took several
weeks (for a hypervisor written in C++ which supported windows a linux
guests - obviously with significant QA required to detect functionality
or performance regressions). The developers time was spent
on the defined product features, not on unnecessary toolchain upgrades.

scott

Tobias Müller

unread,
Jun 25, 2013, 4:38:02 PM6/25/13
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> Jorgen Grahn <grahn...@snipabacken.se> writes:
> Most places I've worked would time the upgrade of a toolchain
> with the introduction of a new product, the existing products
> will continue to use the older toolchain, with both generally
> installed on the build systems. Another nice feature of linux;
> just try to install VS2003 and VS2013 on the same system.

I cannot talk about those specific versions, but we have VS 2005 and VS
2010 installed in parallel and had no problems so far.
Before that, we had VS 6.0 and VS 2005 in parallel and also no problems.

Tobi

Robert Wessel

unread,
Jun 25, 2013, 5:26:09 PM6/25/13
to
ClearPath IX, only, of course, not the ClearPath/MCP (which is the old
Burroughs stuff, not the Univac 2200 stuff). 36 bit ints *and* ones'
complement!

Although honestly, I don't expect any of our code to run there without
a fair bit of tweaking...

Robert Wessel

unread,
Jun 25, 2013, 5:32:43 PM6/25/13
to
On Tue, 25 Jun 2013 20:09:23 GMT, sc...@slp53.sl.home (Scott Lurndal)
Both systems have introduced their share of incompatibilities during
upgrades and patches. At least most Windows kernel drive binaries
don't *need* to be recompiled for every minor patch.


>>
>>Not having a strategy for regular upgrades is a risk.
>
>Most places I've worked would time the upgrade of a toolchain
>with the introduction of a new product, the existing products
>will continue to use the older toolchain, with both generally
>installed on the build systems. Another nice feature of linux;
>just try to install VS2003 and VS2013 on the same system.


While I don't have VS13 installed, I do have VS6, VS03, VS05, VS08 and
VS10 installed on one of my machines. Can't say that I ever really
had an issue.

Juha Nieminen

unread,
Jun 26, 2013, 3:29:06 AM6/26/13
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> g++.
>
> Just because the most recent version has it, doesn't mean that the millions
> of users of the existing and prior versions are going to instantly change
> over. That costs money and it is completely unnecessary.

Right, because gcc is sooo expensive.

Jorgen Grahn

unread,
Jun 26, 2013, 3:32:15 AM6/26/13
to
On Tue, 2013-06-25, Scott Lurndal wrote:
> Jorgen Grahn <grahn...@snipabacken.se> writes:
>>On Tue, 2013-06-25, Scott Lurndal wrote:
>>> Jorgen Grahn <grahn...@snipabacken.se> writes:
>>>>On Mon, 2013-06-24, Scott Lurndal wrote:
>>>>...
>>>>> It will likely be close to a decade before C++11 will be widely
>>>>> enough available (in real shops) such that it can be used in legacy
>>>>> or even new projects.
>>>>
>>>>Which area and kind of shop is this? I'm rather conservative, and
>>>>also pessimistic about large projects keeping their tools up to date
>>>>... but almost a decade seems like an awfully long time.
>>...
>>
>>> Inertia rules.
>>>
>>> Shops are, for the most part, quite conservative. If something works,
>>> don't break it. Changing compilers is not as simple as "yum update";
>>> all the existing projects using the current compiler version need be
>>> updated for the new
>>
>>Yes, but there's the other side of the coin too. The farther between
>>the toolchain/OS upgrades, the more painful they become. And when
>>something finally forces you to do one, it's likely to come at the
>>worst possible time.
>
> Decoupling an OS upgrade from a toolchain upgrade is straightforward
> and common.

Sure -- I didn't mean to claim they belonged together. OS and library
upgrades are /parallels/ to toolchain upgrades.

> And unlike Windows where frequent upgrades and
> incompatibilities are introduced regularly, linux installations can
> survive for a decade without significant disruption (assuming the hardware
> lasts that long).

Yes, and if security fixes are not needed.

>>Not having a strategy for regular upgrades is a risk.
>
> Most places I've worked would time the upgrade of a toolchain
> with the introduction of a new product, the existing products
> will continue to use the older toolchain, with both generally
> installed on the build systems.

I don't mind that strategy, unless new products are introduced very
rarely.

> Another nice feature of linux;
> just try to install VS2003 and VS2013 on the same system.

>>> (with -Wall/-Werror it is quite likely that code
>>> that compiled with the current version won't compile with the next).
>>
>>I've never found that part to be more than an annoyance -- the fixes
>>tend to be obvious, backwards-compatible and review-only. Usually you
>>forgot an #include somewhere.
>
> Annoyance or not, any single checkin will restart the QA process. Which
> takes time and costs money.

Now that I think of it, the toolchain change itself naturally needs a
full test cycle. But it seems to me you can slip this change in
together with some other change. (This implies you probably won't
upgrade the tools for a product in maintenance mode.)

> The developers time was spent
> on the defined product features, not on unnecessary toolchain upgrades.

I object to the "unnecessary" part. It's not unnecessary, it's just
something that's almost never urgent. That doesn't mean it's not
harmful to skip that step.

Ian Collins

unread,
Jun 26, 2013, 3:58:43 AM6/26/13
to
Jorgen Grahn wrote:
> On Tue, 2013-06-25, Scott Lurndal wrote:
>>
>> Annoyance or not, any single checkin will restart the QA process. Which
>> takes time and costs money.
>
> Now that I think of it, the toolchain change itself naturally needs a
> full test cycle. But it seems to me you can slip this change in
> together with some other change. (This implies you probably won't
> upgrade the tools for a product in maintenance mode.)

That has always been my approach; if the product is going to have a full
QA cycle, that's a good time to update tools. No compiler is perfect,
working round rather than patching or upgrading to avoid a compiler bug
is just asking for trouble later.

--
Ian Collins

David Brown

unread,
Jun 26, 2013, 9:19:52 AM6/26/13
to
This last point is key. These sorts of mainframes with weird
architectures are so specialised that most code written for them is
written, or at least modified, specifically for these systems.

Saying that we should not use uint32_t (et.al.) as a way of getting
accurate type sizes because "these types aren't available in all
implementations" is like saying we should not use glasses to correct
eyesight problems because some people don't have ears or have flat noses.



Jorgen Grahn

unread,
Jun 26, 2013, 10:14:51 AM6/26/13
to
On Wed, 2013-06-26, David Brown wrote:
> On 25/06/13 23:26, Robert Wessel wrote:
>> On Tue, 25 Jun 2013 12:35:12 -0700 (PDT), James Kanze
>> <james...@gmail.com> wrote:
>>
>>> On Tuesday, 25 June 2013 18:02:06 UTC+1, David Brown wrote:
>>>> On 25/06/13 17:53, James Kanze wrote:
>>>>> On Monday, 24 June 2013 22:20:19 UTC+1, woodb...@gmail.com wrote:
>>>>>> On Saturday, June 22, 2013 11:05:53 AM UTC, James Kanze wrote:
>>>>>>> On Friday, June 21, 2013 4:45:44 PM UTC+1, Scott Lurndal wrote:
>>>>>>>> The vast majority of types I use in
>>>>>>>> my current projects are unsigned (uint32_t, uint64_t).
>>>
>>>>>>> In other words, types that aren't even available in all
>>>>>>> implementations. These types are only appropriate in very
>>>>>>> limited cases.
>>>
>>>>>> As far as I can tell these types are becoming more widely
>>>>>> available. (I don't care if a platform can't implement all
>>>>>> of them.)
>>>
>>>>> Some platforms can't implement any of them, or only "uint8_t".
>>>
>>>> Can you name a platform that supports standard C types (int of at least
>>>> 16 bits, long int of at least 32-bit, and unsigned versions) that does
>>>> not support at least uint32_t and int32_t ? Preferably one that has
>>>> been used this century?
>>>
>>> [rather far-fetched examples]
>>
>> Although honestly, I don't expect any of our code to run there without
>> a fair bit of tweaking...
>>
>
> This last point is key. These sorts of mainframes with weird
> architectures are so specialised that most code written for them is
> written, or at least modified, specifically for these systems.
>
> Saying that we should not use uint32_t (et.al.) as a way of getting
> accurate type sizes because "these types aren't available in all
> implementations" is [ridiculous]

I don't think anyone is against using them when you need accurate size.
E.g. if your algorithm relies on arithmetics being done modulo 2^16,
it's a good idea to use uint16_t. If you manipulate buffers for some
binary protocol defined in terms of 8-bit octets, it makes sense to use
uint8_t.

The question is whether they should also be used more widely, but I'm
not sure everyone agrees on *which* "more widely" is being
discussed ...


Personally I tend to use 'int' and 'unsigned' a lot, but I should
really add a static assert somewhere saying "I assume unsigned is at
least 32 bits" because that's what I tend to assume. I haven't been
near 16-bit hardware in over a decade.

Partily this is a reaction, I think, to code I've written in the past
filled with WORD, ULONG etc typedefs. Plus the POSIX APIs work that
way.

David Brown

unread,
Jun 26, 2013, 11:21:23 AM6/26/13
to
Long ago, I used to use typedefs like WORD - but they were /my/
typedefs, based on the small processors I used, and so WORD was what is
now called uint16_t. Fortunately I didn't have much need of mixing code
from different sources, or it would have been a big mess. In embedded
software, there are endless variants for fixed size types, such as u32,
uint_32, u_32_t, etc., as well as unnumbered versions like WORD or ULONG.

<stdint.h> has been a huge step forward for embedded development, where
exact size types are the norm rather than the exception.


Scott Lurndal

unread,
Jun 26, 2013, 12:00:31 PM6/26/13
to
Juha Nieminen <nos...@thanks.invalid> writes:
>Scott Lurndal <sc...@slp53.sl.home> wrote:
>> g++.
>>
>> Just because the most recent version has it, doesn't mean that the millions
>> of users of the existing and prior versions are going to instantly change
>> over. That costs money and it is completely unnecessary.
>
>Right, because gcc is sooo expensive.

Yeah, and 1000 man-hours QA time is free.

scott

Öö Tiib

unread,
Jun 26, 2013, 4:47:35 PM6/26/13
to
If you have to fully QA anyway (for releasing new version of your product)
then there are no significant additional costs. More recent version of
compiler usually means improved diagnostics and fixed defects. It more
often adds QA value instead of cost.

The exiting new language features might work not fully conforming at first
and may confuse other tools in your tool-chain. Again easy. Just accept
the features into your coding standard one-by-one. That does not mean that
you have to stick with same old compiler version (or whatever other tool
version) for decades.

Juha Nieminen

unread,
Jun 26, 2013, 5:23:20 PM6/26/13
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
>>Right, because gcc is sooo expensive.
>
> Yeah, and 1000 man-hours QA time is free.

If you need that much work to transition to a newer version of a compiler
(no matter how many people are using it in your firm), then you are
doing it wrong.

Jorgen Grahn

unread,
Jun 27, 2013, 3:28:41 AM6/27/13
to
On Wed, 2013-06-26, David Brown wrote:
> On 26/06/13 16:14, Jorgen Grahn wrote:
...
>> Personally I tend to use 'int' and 'unsigned' a lot, but I should
>> really add a static assert somewhere saying "I assume unsigned is at
>> least 32 bits" because that's what I tend to assume. I haven't been
>> near 16-bit hardware in over a decade.
>>
>> Partily this is a reaction, I think, to code I've written in the past
>> filled with WORD, ULONG etc typedefs. Plus the POSIX APIs work that
>> way.
>
> Long ago, I used to use typedefs like WORD - but they were /my/
> typedefs, based on the small processors I used, and so WORD was what is
> now called uint16_t. Fortunately I didn't have much need of mixing code
> from different sources, or it would have been a big mess.

Same here, except they weren't mine. The AmigaDOS APIs used WORD,
LONG etc everywhere, so I did too. It became a minor obstacle when I
later wanted to port my code to Unix, and didn't feel like dragging in
an AmigaDOS compat header.

> In embedded
> software, there are endless variants for fixed size types, such as u32,
> uint_32, u_32_t, etc., as well as unnumbered versions like WORD or ULONG.
>
> <stdint.h> has been a huge step forward for embedded development, where
> exact size types are the norm rather than the exception.

It may be the norm, but is there a good reason it /has/ to be that
way? In an embedded project,

- You typically have complete or partial knowledge about the CPU and
compiler, beyond what C or even POSIX guarantees.
- You may have to deal with memory-mapped resources. Possibly you
know your compiler's struct layout too.
- You may need to minimize the size of long-lived data structures.

But it doesn't follow that exact sizes /need/ to be the norm.

A counterexample: the Linux kernel is "embedded" as I understand the
word. If I pick a file at semi-random (kernel/sched/core.c) I see
almost exclusively char, (unsigned) int and (unsigned) long. Some
size_t and ssize_t too. It uses u64 and s64 when a 32-bit type would
not be large enough, such as nanosecond time delays.

Jorgen Grahn

unread,
Jun 27, 2013, 3:51:04 AM6/27/13
to
On Wed, 2013-06-26, Juha Nieminen wrote:
> Scott Lurndal <sc...@slp53.sl.home> wrote:
>>>Right, because gcc is sooo expensive.
>>
>> Yeah, and 1000 man-hours QA time is free.
>
> If you need that much work to transition to a newer version of a compiler
> (no matter how many people are using it in your firm), then you are
> doing it wrong.

He (or rather his project) is, but so is pretty much everyone else as
far as I can tell.

I've never seen a largish embedded project with some years of baggage
where a full regression test cycle took less effort. I've been working
with decreasing the cost of it in some projects, but it's not easy.
Automation is only part of the answer, and comes with problems of its
own ...

Remember, we're not just talking about unit tests or purely functional
tests: there are things like long-term stability, robustness (if I
introduce this serious error condition, how bad are the consequences?),
performance ... hell, even security ...

Gerhard Fiedler

unread,
Jun 27, 2013, 11:34:59 AM6/27/13
to
Jorgen Grahn wrote:

> On Wed, 2013-06-26, David Brown wrote:
>> In embedded software, there are endless variants for fixed size
>> types, such as u32, uint_32, u_32_t, etc., as well as unnumbered
>> versions like WORD or ULONG.
>>
>> <stdint.h> has been a huge step forward for embedded development,
>> where exact size types are the norm rather than the exception.
>
> It may be the norm, but is there a good reason it /has/ to be that
> way? In an embedded project,
>
> - You typically have complete or partial knowledge about the CPU and
> compiler, beyond what C or even POSIX guarantees.
> - You may have to deal with memory-mapped resources. Possibly you
> know your compiler's struct layout too.
> - You may need to minimize the size of long-lived data structures.
>
> But it doesn't follow that exact sizes /need/ to be the norm.

Doesn't directly. It depends on what you consider an "embedded system".
I think currently, small 8-bit processors are still the majority of
embedded systems out there. C code often runs on different models of
processors, often also on completely different families. Due to resource
constraints, algorithms are often written specifically to keep resource
usage to a minimum -- using exactly these types, and even add 24-bit
integers to the mix of available data sizes (which of course on 32-bit
and bigger systems don't make any sense).

You of course generally have the compiler's guarantees for a given
target, but rather than having to remember what this compiler for this
target understands as int and that compiler for that target, it's just
so much easier to use int16_t if you need a 16-bit int... Why
remembering all that crap? You still need to keep the target
architecture in mind, but adding compiler specifics to the mix of what
you need to keep in mind is just unnecessary.

> A counterexample: the Linux kernel is "embedded" as I understand the
> word. If I pick a file at semi-random (kernel/sched/core.c) I see
> almost exclusively char, (unsigned) int and (unsigned) long. Some
> size_t and ssize_t too. It uses u64 and s64 when a 32-bit type would
> not be large enough, such as nanosecond time delays.

Right. You can also have a Cray embedded somewhere. "Embedded" by itself
doesn't say much. (FWIW, the Linux kernel is not normally "embedded" as
I understand it. Normally it runs on desktop or server type machines,
which is not what AIUI the term "embedded systems" is normally used for:
http://en.wikipedia.org/wiki/Embedded_system. But there are of course
tons of embedded systems in this meaning that do run the Linux kernel.)

Gerhard

David Brown

unread,
Jun 27, 2013, 3:08:03 PM6/27/13
to
That's certainly true. But sometimes you want to re-use at least some
code between different architectures.

> - You may have to deal with memory-mapped resources. Possibly you
> know your compiler's struct layout too.

That is regularly the case. I invariably have "-Wpadded" enabled with
gcc, to be sure that there are no unexpected alignment paddings in my
structures (and I often use static assertions as a double-check and for
documentation purposes).

> - You may need to minimize the size of long-lived data structures.

You often want to minimise resource usage.

>
> But it doesn't follow that exact sizes /need/ to be the norm.

I think that very often, "int_fast16_t" and related types would be the
best choice for a lot of code, as it says exactly what you want it to
say. But in practice, precisely because (as you say) you generally know
the target cpu and compiler very well, you know whether this is the same
as int16_t or int32_t, and use those types because they are smaller and
neater to write, and the exact size ensures you avoid surprises.

Also, on occasion, you know that on your target, particular sizes are
faster (or smaller code) for particular operations. For example, on a
32-bit cpu you might find that 16-bit array indexes are actually faster
than 32-bit ones - or maybe you have a 16-bit databus and therefore can
read and write 16-bit data faster than 32-bit data (this is not so
common these days, but used to be a common arrangement).

>
> A counterexample: the Linux kernel is "embedded" as I understand the
> word. If I pick a file at semi-random (kernel/sched/core.c) I see
> almost exclusively char, (unsigned) int and (unsigned) long. Some
> size_t and ssize_t too. It uses u64 and s64 when a 32-bit type would
> not be large enough, such as nanosecond time delays.
>
> /Jorgen
>

The kernel types were picked long before <stdint.h> types were in common
use. But generally the size-specific types are used when exact sizes
are needed (especially for target-independent data, such as filesystem
structures) - and a few different types are used to indicate different
purposes (different type names are used for kernel types and user-level
types).

In most ways, standard C "int" is like "int_fast32_t" on a 32-bit
system. So when you know your system uses 32-bit ints, like all Linux
systems (whether on 32-bit or 64-bit targets), it is quite natural to
stick to "int".


Jorgen Grahn

unread,
Jun 28, 2013, 4:13:34 AM6/28/13
to
On Thu, 2013-06-27, David Brown wrote:
> On 27/06/13 09:28, Jorgen Grahn wrote:
>> On Wed, 2013-06-26, David Brown wrote:

>>> In embedded
>>> software, there are endless variants for fixed size types, such as u32,
>>> uint_32, u_32_t, etc., as well as unnumbered versions like WORD or ULONG.
>>>
>>> <stdint.h> has been a huge step forward for embedded development, where
>>> exact size types are the norm rather than the exception.
>>
>> It may be the norm, but is there a good reason it /has/ to be that
>> way? In an embedded project,
>>
>> - You typically have complete or partial knowledge about the CPU and
>> compiler, beyond what C or even POSIX guarantees.
>
> That's certainly true. But sometimes you want to re-use at least some
> code between different architectures.
>
>> - You may have to deal with memory-mapped resources. Possibly you
>> know your compiler's struct layout too.

> That is regularly the case. I invariably have "-Wpadded" enabled with
> gcc, to be sure that there are no unexpected alignment paddings in my
> structures (and I often use static assertions as a double-check and for
> documentation purposes).

-Wpadded
Warn if padding is included in a structure, either to align an
element of the structure or to align the whole structure. [...]

I didn't know about that one. Seems to me you'd have to add padding
manually in many cases to make the compiler shut up? The static assert
way seems more attractive.

>> - You may need to minimize the size of long-lived data structures.
>
> You often want to minimise resource usage.
>
>> But it doesn't follow that exact sizes /need/ to be the norm.
>
> I think that very often, "int_fast16_t" and related types would be the
> best choice for a lot of code, as it says exactly what you want it to
> say. But in practice, precisely because (as you say) you generally know
> the target cpu and compiler very well, you know whether this is the same
> as int16_t or int32_t, and use those types because they are smaller and
> neater to write,

Yes. I don't think I've ever seen the fast/least variants in real
code, and the cumbersome names are surely a major part of the reason.
Another can be that people still think in terms of u32 or WORD, or
whatever they used before.

> and the exact size ensures you avoid surprises.

...
>> A counterexample: the Linux kernel is "embedded" as I understand the
>> word. If I pick a file at semi-random (kernel/sched/core.c) I see
>> almost exclusively char, (unsigned) int and (unsigned) long. Some
>> size_t and ssize_t too. It uses u64 and s64 when a 32-bit type would
>> not be large enough, such as nanosecond time delays.
...
> The kernel types were picked long before <stdint.h> types were in common
> use.

Yes, but here I see no significant difference between the exact stdint
types and the kernel's home-made exact types (u32, s16 etc).

> But generally the size-specific types are used when exact sizes
> are needed (especially for target-independent data, such as filesystem
> structures) - and a few different types are used to indicate different
> purposes (different type names are used for kernel types and user-level
> types).
>
> In most ways, standard C "int" is like "int_fast32_t" on a 32-bit
> system. So when you know your system uses 32-bit ints, like all Linux
> systems (whether on 32-bit or 64-bit targets), it is quite natural to
> stick to "int".

Yes, I agree with that description. And I think you can often use the
same principles in other[1] embedded work.

/Jorgen

[1] Yes, I still count kernel programming as "embedded" work. And yes,
I am aware that there are systems which have much more severe
restrictions.

David Brown

unread,
Jun 28, 2013, 5:32:34 AM6/28/13
to
Yes, I add any padding manually to keep it explicit. That way there is
never any question of how the structure is actually laid out. There are
at least three circumstances where this is important - matching hardware
registers, matching system-independent and shared data (such as
telegrams or communication objects), and matching exact sizes for
efficiency (on some processors, it may be very much faster to work with
an array of structs size 16 bytes rather than size 14 bytes, for
example). In other cases, of course, the details don't matter -
typically data is laid out from high alignment bits to small alignment
bits so there is no need for any padding.

Static assertions are very nice to check correctness, and to document
assumptions.

>>> - You may need to minimize the size of long-lived data structures.
>>
>> You often want to minimise resource usage.
>>
>>> But it doesn't follow that exact sizes /need/ to be the norm.
>>
>> I think that very often, "int_fast16_t" and related types would be the
>> best choice for a lot of code, as it says exactly what you want it to
>> say. But in practice, precisely because (as you say) you generally know
>> the target cpu and compiler very well, you know whether this is the same
>> as int16_t or int32_t, and use those types because they are smaller and
>> neater to write,
>
> Yes. I don't think I've ever seen the fast/least variants in real
> code, and the cumbersome names are surely a major part of the reason.
> Another can be that people still think in terms of u32 or WORD, or
> whatever they used before.

I've used the fast variants occasionally, but I haven't got used to the
cumbersome names (as you say). I can't say I have ever seen a use for
the "least" variants - that would have to be for code that requires a
type with minimal size but where you are not sure that size exists.
Perhaps you want minimal size data on a DSP that doesn't support 8-bit
or 16-bit data. I hopefully will never come across such situations (I
avoid such DSP's like the plague).

>
>> and the exact size ensures you avoid surprises.
>
> ...
>>> A counterexample: the Linux kernel is "embedded" as I understand the
>>> word. If I pick a file at semi-random (kernel/sched/core.c) I see
>>> almost exclusively char, (unsigned) int and (unsigned) long. Some
>>> size_t and ssize_t too. It uses u64 and s64 when a 32-bit type would
>>> not be large enough, such as nanosecond time delays.
> ...
>> The kernel types were picked long before <stdint.h> types were in common
>> use.
>
> Yes, but here I see no significant difference between the exact stdint
> types and the kernel's home-made exact types (u32, s16 etc).
>

Linux uses a few different types here that are identical to the
compiler, but have different names, almost as a documentation feature.
The idea is to make it clear what kind of data it is (i.e., kernel data,
user data, etc.). It's a bit like using "handle" as a type even if it
is typedef'ed to "int". I can't say I have studied enough Linux kernel
code to know the details, but apparently they find the conventions useful.

>> But generally the size-specific types are used when exact sizes
>> are needed (especially for target-independent data, such as filesystem
>> structures) - and a few different types are used to indicate different
>> purposes (different type names are used for kernel types and user-level
>> types).
>>
>> In most ways, standard C "int" is like "int_fast32_t" on a 32-bit
>> system. So when you know your system uses 32-bit ints, like all Linux
>> systems (whether on 32-bit or 64-bit targets), it is quite natural to
>> stick to "int".
>
> Yes, I agree with that description. And I think you can often use the
> same principles in other[1] embedded work.
>

Even though I generally know that a particular piece of code that I
write will only be run on a 16-bit, 8-bit or 32-bit cpu, I prefer to
keep a consistent style amongst them and use fixed size types. Personal
habit, I suppose.

James Kanze

unread,
Jun 28, 2013, 8:02:07 AM6/28/13
to
The old Burroughs stuff was 48 bit signed magnitude, with 8 bits
required to be 0. It was the architecture I had in mind for
"only uint8_t". The C standard requires 1) exact size, with all
bits significant, and 2) 2's complement for the signed types.

I've often wished I could program on that architecture. If I
understand it correctly, when you overflow integral arithmetic,
the hardware automatically treats it a floating point: whether
a variable has type int or type float, or for that matter, is a
pointer, depends on the bit pattern in the 48 bit word. And the
machine has no hardware unsigned arithmetic; the unsigned types
are emulated by and'ing out the sign bit after each operation.

> Although honestly, I don't expect any of our code to run there without
> a fair bit of tweaking...

I think that most of my older code would (but of course, I can
only be sure by trying it). A lot of the recent stuff does
assume IEEE float and double. (Writing truly portable floating
point must be exceedingly difficult, because the base affects
rounding, and you proving certain algorithms converge not only
with real numbers, but also with machine floating point requires
knowing how the floating point rounds.) And almost all assumes
that int is at least 32 bits, but then, it requires a reasonably
sized general purpose machine to run it.

--
James

James Kanze

unread,
Jun 28, 2013, 8:25:12 AM6/28/13
to
You've obviously not been following the discussion. Others are
claiming that they should be used *for maximum portability*.
I'm just saying that this isn't the case. Most of the time, you
don't need maximum portability. And it is very rare that you
need exact sized int or 2's complement, at least if you write
your code cleanly. (There are exceptions, of course, especially
in low level code. But they don't apply to most applications.)

The standard type for integral values is int. That's how the
language was designed. The other integral types are for special
uses. One can argue that this is not good design, and that we
need a "cardinal" type, like Pascal had, which *is* appropriate
for general use where negative values don't make sense, but it's
not going to happen.

--
James

James Kanze

unread,
Jun 28, 2013, 8:40:44 AM6/28/13
to
On Thursday, June 27, 2013 8:08:03 PM UTC+1, David Brown wrote:
> On 27/06/13 09:28, Jorgen Grahn wrote:
> > On Wed, 2013-06-26, David Brown wrote:
> >> On 26/06/13 16:14, Jorgen Grahn wrote:
> > ...
> >> In embedded
> >> software,

Just a quick note, but embedded systems do obey somewhat
different rules than general purpose systems.

> >> there are endless variants for fixed size types, such as u32,
> >> uint_32, u_32_t, etc., as well as unnumbered versions like WORD or ULONG.

> >> <stdint.h> has been a huge step forward for embedded development, where
> >> exact size types are the norm rather than the exception.

> > It may be the norm, but is there a good reason it /has/ to be that
> > way? In an embedded project,

> > - You typically have complete or partial knowledge about the CPU and
> > compiler, beyond what C or even POSIX guarantees.

> That's certainly true. But sometimes you want to re-use at least some
> code between different architectures.

And depending on the case, you might want to have different
sizes for your values on the different architectures. I've a
lot of code that I wrote for machines with 16 bit ints, way back
when. It still works, and more importantly, it takes advantage
of the larger int on todays machines. The fact that you target
different platforms with different capabilities is one of the
strongest arguments I know *against* the fixed size types. (But
it depends on what you are doing. If you're positioning bits to
write to an 8 bit hardware port, I'd definitely use uint8_t.
Since presumably, the size of the hardware port won't change
when I change CPU architectures.)

[...]
> > But it doesn't follow that exact sizes /need/ to be the norm.

> I think that very often, "int_fast16_t" and related types would be the
> best choice for a lot of code, as it says exactly what you want it to
> say. But in practice, precisely because (as you say) you generally know
> the target cpu and compiler very well, you know whether this is the same
> as int16_t or int32_t, and use those types because they are smaller and
> neater to write, and the exact size ensures you avoid surprises.

Actually, I can't imagine a case where "int_fast16_t" would be
anything but "int". "int" is required to be at least 16 bits,
and as the "natural" type, it should be the fastest integral
type.

At least on "simple" machines. Multilevel memory and pipelined
memory access has made locality an issue, to the point where the
fastest (or most natural) type depends a lot on what you're
doing with it.

--
James

David Brown

unread,
Jun 28, 2013, 9:52:35 AM6/28/13
to
On 28/06/13 14:40, James Kanze wrote:
> On Thursday, June 27, 2013 8:08:03 PM UTC+1, David Brown wrote:
>> On 27/06/13 09:28, Jorgen Grahn wrote:
<snip>
>> I think that very often, "int_fast16_t" and related types would be the
>> best choice for a lot of code, as it says exactly what you want it to
>> say. But in practice, precisely because (as you say) you generally know
>> the target cpu and compiler very well, you know whether this is the same
>> as int16_t or int32_t, and use those types because they are smaller and
>> neater to write, and the exact size ensures you avoid surprises.
>
> Actually, I can't imagine a case where "int_fast16_t" would be
> anything but "int". "int" is required to be at least 16 bits,
> and as the "natural" type, it should be the fastest integral
> type.
>
> At least on "simple" machines. Multilevel memory and pipelined
> memory access has made locality an issue, to the point where the
> fastest (or most natural) type depends a lot on what you're
> doing with it.
>

I can think of a system I have worked with where "int" is 32-bit, but
"int_fast16_t" may be 16-bit - and certainly 16-bit ints would be faster
than 32-bit ints for some uses (and slower for others). We used to make
heavy use of the 68332, which is roughly like a 68020 but with a 16-bit
external databus (and of course lots of peripherals built-in). If a
value stays in registers, 32-bit and 16-bit are equally fast (with a
slight margin for the 32-bit data in a few cases) - but if it goes out
to memory or stack, then 16-bit will be nearly twice as fast. But being
a 32-bit cpu, it has 32-bit "int".

I haven't used systems with that sort of complication for a good while -
but if a cpu can work with 16-bit data just as quickly as 32-bit data, I
don't see why int_fast16_t would not be 16-bit.

However, I can't think of any situation where you would want to use
"int_fast16_t" but would not be happy with "int" - as choosing
"int_fast16_t" says you don't mind if it is bigger than 16-bit, just as
long as it is fast.

David Brown

unread,
Jun 28, 2013, 10:09:05 AM6/28/13
to
It is a lot to follow - I have certainly glossed over much of it.

> Others are
> claiming that they should be used *for maximum portability*.
> I'm just saying that this isn't the case. Most of the time, you
> don't need maximum portability.

These are not contradictory choices. You need size-specific types for
maximum portability - of that I have no doubt. "Maximum portability",
of course, does not imply "absolute portability" - there will always be
weird architectures for which things are awkward, but it is extremely
rare for code to need portability between 8-bit embedded AVR
microcontrollers and mainframes from the 1970's.

Equally, you are correct that most of the time you don't need "maximum
portability" - most code is either written for "big" machines, or for
"small embedded" systems, and does not have to be portable across these
categories. It is also common for embedded code to be specific to a
particular target.

So /if/ you need to write highly portable code, then you need to use
size-specific types for at least some of your data.

> And it is very rare that you
> need exact sized int or 2's complement, at least if you write
> your code cleanly. (There are exceptions, of course, especially
> in low level code. But they don't apply to most applications.)

For general purpose code, "big system" application code, etc., then
that's true. For embedded development (especially on smaller systems),
it is much more common to need to use size-specific sizes and require
2's complement, wrap-around integer overflows, etc. I suppose that is
your definition of "low level code" here.

>
> The standard type for integral values is int. That's how the
> language was designed. The other integral types are for special
> uses. One can argue that this is not good design, and that we
> need a "cardinal" type, like Pascal had, which *is* appropriate
> for general use where negative values don't make sense, but it's
> not going to happen.
>

Again, the situation depends on your viewpoint and the systems you are
working on - and small-system embedded development is different from
programming for Linux, Windows, etc. On many small processors, code is
more efficient if unsigned types are used, and in particular if
"uint8_t" is used whenever possible.

It is also good design to use "unsigned" types when your data cannot be
negative, since it lets the compiler check that possibility. And it is
often good design to use unsigned types if the extra bit of range is
useful. I've had to work with languages that don't support unsigned
types, and some types of code are horrible to deal with.

It would be nice if C and C++ supported ranged types, like Pascal or Ada
- at least to allow static compiler checks on the data. I suppose such
beasties could be created quite efficiently now with C++11, using
templates with constexpr, perhaps __builtin_constant_p with gcc, and
"explicit" on constructors and type conversion operators.

Scott Lurndal

unread,
Jun 28, 2013, 10:12:08 AM6/28/13
to
I'm claiming they should be used for *maximum correctness*.

scott

Paavo Helde

unread,
Jun 28, 2013, 1:53:36 PM6/28/13
to
David Brown <da...@westcontrol.removethisbit.com> wrote in
news:5cudnVDG276fBFDM...@lyse.net:

> These are not contradictory choices. You need size-specific types for
> maximum portability - of that I have no doubt. "Maximum portability",
> of course, does not imply "absolute portability" - there will always
> be weird architectures for which things are awkward, but it is
> extremely rare for code to need portability between 8-bit embedded AVR
> microcontrollers and mainframes from the 1970's.

You are mixing up cause and consequence. Such portability is rare because
it is hard or impossible to achieve, so nobody is even attempting it. This
does not mean that there is no need. From the "code viewpoint" it always
needs all the portability it can get, this is a survival issue.

Cheers
Paavo

David Brown

unread,
Jun 28, 2013, 2:50:30 PM6/28/13
to
On 28/06/13 16:12, Scott Lurndal wrote:
> James Kanze <james...@gmail.com> writes:
>> On Wednesday, June 26, 2013 2:19:52 PM UTC+1, David Brown wrote:
>>> On 25/06/13 23:26, Robert Wessel wrote:
<snip>
>>> Saying that we should not use uint32_t (et.al.) as a way of getting
>>> accurate type sizes because "these types aren't available in all
>>> implementations" is like saying we should not use glasses to correct
>>> eyesight problems because some people don't have ears or have flat noses.
>>
>> You've obviously not been following the discussion. Others are
>> claiming that they should be used *for maximum portability*.
>
> I'm claiming they should be used for *maximum correctness*.
>

I support that claim. I also think that fixed size types are not just
important to correctness, but important to making it /clear/ that the
code is correct. They help you see exactly what you are doing, and to
be sure that you get exactly what you want.


David Brown

unread,
Jun 28, 2013, 2:57:00 PM6/28/13
to
No, I am not mixing these up. If I had good cause to write code that
worked across such disparate systems, I would - there are a number of
examples of projects that are designed to compile across large numbers
of compilers and targets. (newlib and boost are two such cases.) But
apart from a few very general purpose libraries, there is no need for
such a wide portability range because there is no type of application
that would make sense to run on a small microcontroller and a dinosaur
mainframe.

(Note that many microcontrollers these days are "normal" 32-bit cpus,
and most "big iron" systems are "normal" 32-bit or 64-bit cpus - it's
very easy to make code portable across these.)


Scott Lurndal

unread,
Jun 28, 2013, 3:41:02 PM6/28/13
to
I've done a simulator in C++ for one of those "Dinosaur mainframes". A physical
address resolves to a 4-bit field. The instruction set can operate on
from one to 100 of these 4-bit fields (or 1 to 100 8-bit "bytes"), with
arithmetic data stored as BCD or zoned EBCDIC.

While one can represent a 32-bit integer as 8 4-bit digits, the fact that
they're stored in BCD rather than binary leads to a basic difficulty
with the <stdint.h> types; a uint32_t field will have a maximum value
of only 99,999,999 (the processor did have D2B and B2D conversion
instructions, but all arithmetic was done in BCD).

All this convinced me back in the early 80's not to bother with a C
compiler port to this architecture :-) COBOL on the other hand
was a natural fit (back then, the architecture simulators were written
in ALGOL, BPL and Simula).

http://vseries.lurndal.org/doku.php?id=instructions

Ian Collins

unread,
Jun 28, 2013, 4:27:33 PM6/28/13
to
I can see and generally agree with both your and James' side of the
argument! You are looking at the issue from an embedded programmer's
perspective and James from an application programmer's perspective.

Within my embedded or driver projects, fixed width types feature
extensively. They (with the exception of the 64 bit types, which are a
spacial case) are conspicuously absent form my higher level application
code.

--
Ian Collins

Öö Tiib

unread,
Jun 28, 2013, 4:40:44 PM6/28/13
to
I need array that may have up to 20000 elements. To indicate the state
of the array I use variable that is valid in range from 0 to 20000.
How is using 'int16_t' or 'uint16_t' (or whatever you suggest) making it
*clear* that the code is correct?

How is using 'int', 'ssize_t' or 'size_t' (or whatever you suggest)
making it *unclear*? 'int' is most logical and most simple choice.

To ensure maximum correctness I write simple static_assert that the
required limits fit into numeric_limits<int> and a pile of unit tests.
The fixed width types are no way magic bullet and help from them is
minimal.

When a fixed width type is exact match is rare exception. See for
example C++ standard Annex B about quantities. Suggested minimum
quantities are mostly exactly one larger than largest value that
fits into some most beloved amounts of bits (like 8, 10, 16). Probably
the pun was intended!

woodb...@gmail.com

unread,
Jun 28, 2013, 5:53:50 PM6/28/13
to
On Friday, June 28, 2013 8:27:33 PM UTC, Ian Collins wrote:

> Within my embedded or driver projects, fixed width types feature
> extensively. They (with the exception of the 64 bit types, which are a
> spacial case) are conspicuously absent form my higher level application
> code.
>

Most applications have some sort of messaging. What integer
types do you use there?


Brian
Ebenezer Enterprises - Where there's no vision the people perish.
http://webEbenezer.net

Ian Collins

unread,
Jun 28, 2013, 7:23:27 PM6/28/13
to
woodb...@gmail.com wrote:
> On Friday, June 28, 2013 8:27:33 PM UTC, Ian Collins wrote:
>
>> Within my embedded or driver projects, fixed width types feature
>> extensively. They (with the exception of the 64 bit types, which are a
>> spacial case) are conspicuously absent form my higher level application
>> code.
>
> Most applications have some sort of messaging. What integer
> types do you use there?

Almost everything I do these days uses serialised JSON or BSON for
messaging. Internally my JSON object class uses long long for integer
types.

--
Ian Collins

David Brown

unread,
Jun 29, 2013, 10:12:30 AM6/29/13
to
On 28/06/13 22:40, �� Tiib wrote:
> On Friday, 28 June 2013 21:50:30 UTC+3, David Brown wrote:
>> On 28/06/13 16:12, Scott Lurndal wrote:
>>> James Kanze <james...@gmail.com> writes:
>>>> You've obviously not been following the discussion. Others are
>>>> claiming that they should be used *for maximum portability*.
>>>
>>> I'm claiming they should be used for *maximum correctness*.
>>
>>
>> I support that claim. I also think that fixed size types are not just
>> important to correctness, but important to making it /clear/ that the
>> code is correct. They help you see exactly what you are doing, and to
>> be sure that you get exactly what you want.
>
> I need array that may have up to 20000 elements. To indicate the state
> of the array I use variable that is valid in range from 0 to 20000.
> How is using 'int16_t' or 'uint16_t' (or whatever you suggest) making it
> *clear* that the code is correct?

If your array has 40000, then using int32_t or uint16_t makes it clear
that the index type will work properly - using "int" does not.

>
> How is using 'int', 'ssize_t' or 'size_t' (or whatever you suggest)
> making it *unclear*? 'int' is most logical and most simple choice.

These sorts of types are okay on their own, but as soon as you have to
store them, convert them, manipulate them, or do arithmetic on them, it
is easier to see correctness if you know exactly what type sizes you have.

>
> To ensure maximum correctness I write simple static_assert that the
> required limits fit into numeric_limits<int> and a pile of unit tests.
> The fixed width types are no way magic bullet and help from them is
> minimal.

So you have a static assertion somewhere to ensure that (for example)
"int" is 32-bit, and then later in the code you use "int" knowing it is
32-bit? What possible benefit is that over writing "int32_t" when you
want a 32-bit integer? "int32_t" says exactly what you want - it is the
clearest, most explicit, and least ambiguous way to get this behaviour.
Additionally, your code is more portable - in particular, you can
re-use parts of it independently.

I agree that fixed width types are not magic bullets - they solve only
one particular issue, but they solve it pretty much perfectly. And of
course I agree on the benefits of static assertion and unit tests - but
these are not magic bullets either. Every little bit helps.

Bo Persson

unread,
Jun 29, 2013, 11:40:07 AM6/29/13
to
David Brown skrev 2013-06-29 16:12:
> On 28/06/13 22:40, �� Tiib wrote:
>> On Friday, 28 June 2013 21:50:30 UTC+3, David Brown wrote:
>>> On 28/06/13 16:12, Scott Lurndal wrote:
>>>> James Kanze <james...@gmail.com> writes:
>>>>> You've obviously not been following the discussion. Others are
>>>>> claiming that they should be used *for maximum portability*.
>>>>
>>>> I'm claiming they should be used for *maximum correctness*.
>>>
>>>
>>> I support that claim. I also think that fixed size types are not just
>>> important to correctness, but important to making it /clear/ that the
>>> code is correct. They help you see exactly what you are doing, and to
>>> be sure that you get exactly what you want.
>>
>> I need array that may have up to 20000 elements. To indicate the state
>> of the array I use variable that is valid in range from 0 to 20000.
>> How is using 'int16_t' or 'uint16_t' (or whatever you suggest) making it
>> *clear* that the code is correct?
>
> If your array has 40000, then using int32_t or uint16_t makes it clear
> that the index type will work properly - using "int" does not.
>

Using a fixed size type doesn't help either.

On a system where int doesn't work as an index, you will likely not be
able to have an array of 40000 elements anyway.


Bo Persson


It is loading more messages.
0 new messages