Many C std. lib. functions accept and/or return size_t values esp. for
arrays incl. char* strings.
In view of this I'm using size_t throughout my code for array sizes and indexes.
However, this means I have to be very careful never to decrement a size_t of value 0, since, e.g., size_t size = 0; size--; results in size == 18446744073709551615.
So I need to guard against this. Here is an example I'm using
(without the assert()s):
[...]
for (size_t i = v->_size - 1; i >= index; --i) {
v->_values[i + 1] = v->_values[i];
if (!i) // if i == 0, --i will wrap!
break;
}
Many C std. lib. functions accept and/or return size_t values esp. for
arrays incl. char* strings.
In view of this I'm using size_t throughout my code for array sizes
and indexes.
However, this means I have to be very careful never to decrement a
size_t of value 0, since, e.g., size_t size = 0; size--; results in
size == 18446744073709551615.
So I need to guard against this. Here is an example I'm using
(without the assert()s):
void vec_insert(vec* v, size_t index, void* value) {
if (v->_size == v->_cap) {
vec_grow(v);
}
for (size_t i = v->_size - 1; i >= index; --i) {
v->_values[i + 1] = v->_values[i];
if (!i) // if i == 0, --i will wrap!
break;
}
v->_values[index] = value;
v->_size++;
}
I've also noticed that quite a few array-related algorithms _assume_
that indexes are signed, so again I have to put in guards to avoid subtracting below zero when I use size_t when implementing them.
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
Substitution: j = i + 1
for (size_t j = v->_size; j > index; --j) {
v->_values[j] = v->_values[j - 1];
}
I've also noticed that quite a few array-related algorithms
_assume_ that indexes are signed, so again I have to put in
guards to avoid subtracting below zero when I use size_t when
implementing them.
So is it considered best practice to use int, long, long long,
or size_t, in situations like these?
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
Mark Summerfield <mark@qtrac.eu> wrote or quoted:
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
Mark Summerfield <mark@qtrac.eu> wrote or quoted:
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C and
just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
On 18 Aug 2024 12:17:36 GMT
ram@zedat.fu-berlin.de (Stefan Ram) wrote:
Mark Summerfield <mark@qtrac.eu> wrote or quoted:
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write
downcounting for() loops in intuitive manner.
Michael S <already5chosen@yahoo.com> writes:
On 18 Aug 2024 12:17:36 GMT
ram@zedat.fu-berlin.de (Stefan Ram) wrote:
Mark Summerfield <mark@qtrac.eu> wrote or quoted:
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other
language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write downcounting for() loops in intuitive manner.
Kind of a funny use of the word intuitive, for two reasons.
The first is that writing for() loops, especially C for() loops,
is learned behavior. There can be patterns that one is used to,
but they are not "intuitive" in the usual sense of the word.
The second is that people who program in C are accustomed to the
idea of an asymmetry between counting up and counting down,
because of how pointers work. It's okay to increment a pointer
to one past the end of an array; it is not okay to decrement a
pointer to one before the beginning of an array. Because of that
the patterns for going forward and for going backward are just
different. It seems odd to use the word "intuitive" to recognize
that distinction.
Which is not to say I disagree with what you are saying. Actually
I guess I'd have to say I'm not sure what it is you are saying.
To my way of thinking the function above doesn't change the way I
would write down-counting loops. It might be useful as a debugging
aid aide, but nothing more (and an assert() is probably better).
I think though that what you're saying is something else but I'm
not sure what it is.
On Sun, 18 Aug 2024 15:23:58 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On 18 Aug 2024 12:17:36 GMT
ram@zedat.fu-berlin.de (Stefan Ram) wrote:
Mark Summerfield <mark@qtrac.eu> wrote or quoted:
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other
language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write
downcounting for() loops in intuitive manner.
Kind of a funny use of the word intuitive, for two reasons.
The first is that writing for() loops, especially C for() loops,
is learned behavior. There can be patterns that one is used to,
but they are not "intuitive" in the usual sense of the word.
The second is that people who program in C are accustomed to the
idea of an asymmetry between counting up and counting down,
because of how pointers work. It's okay to increment a pointer
to one past the end of an array; it is not okay to decrement a
pointer to one before the beginning of an array. Because of that
the patterns for going forward and for going backward are just
different. It seems odd to use the word "intuitive" to recognize
that distinction.
I would think that very large part of 'C' programmers ignores this
asymmetry. They are helped by the fact that 100% of production 'C'
compilers ignore it as well, which means that in practice code that
compares &arr[0] with &arr[-1] works as naively expected on all targets
that have flat memory model and far more often than not works on more
tricky targets as well.
Which is not to say I disagree with what you are saying. Actually
I guess I'd have to say I'm not sure what it is you are saying.
To my way of thinking the function above doesn't change the way I
would write down-counting loops. It might be useful as a debugging
aid aide, but nothing more (and an assert() is probably better).
I think though that what you're saying is something else but I'm
not sure what it is.
Nothing fancy. Just an ability to write downcounting loops in a
way that I, obviously mistakenly, consider intuitive.
for (i = len-1; i >= 0; --i)
However, this means I have to be very careful never to decrement a size_t of value 0, since, e.g., size_t size = 0; size--; results in size == 18446744073709551615.
So I need to guard against this. Here is an example I'm using
(without the assert()s):
void vec_insert(vec* v, size_t index, void* value) {
if (v->_size == v->_cap) {
vec_grow(v);
}
for (size_t i = v->_size - 1; i >= index; --i) {
v->_values[i + 1] = v->_values[i];
if (!i) // if i == 0, --i will wrap!
break;
}
v->_values[index] = value;
v->_size++;
}
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
On 08/20/24 6:53 AM, Andrey Tarasevich wrote:
For example, iteration all the way to 0 can be idiomatically
implemented as
for (some_unsigned_type i = size; (some_unsigned_type) i != -1; --i)
...
Sorry, a typo. Was meant to be
for (some_unsigned_type i = size; i != (some_unsigned_type) -1; --i)
...
For example, iteration all the way to 0 can be idiomatically implemented as
for (some_unsigned_type i = size; (some_unsigned_type) i != -1; --i)
...
So for my arrays and loops I'll change to ptrdiff_t.
On 08/18/24 1:03 AM, Mark Summerfield wrote:
However, this means I have to be very careful never to decrement a
size_t of value 0, since, e.g., size_t size = 0; size--; results in
size == 18446744073709551615.
That's a completely incorrect conclusion. There's nothing wrong
with decrementing a 0 of type `size_t`. It results in perfectly
defined behavior. It produces a `(size_t) -1` value.
For example, iteration all the way to 0 can be idiomatically
implemented as
for (some_unsigned_type i = size; (some_unsigned_type) i != -1; --i)
...
This will work, even though it will eventually decrement a zero
value. If you are sure that the type is "large" (e.g. `int` or
larger), then the cast is unnecessary
for (some_unsigned_type i = size; i != -1; --i)
...
(Note, BTW, that it also works perfectly for signed index types.)
So I need to guard against this. Here is an example I'm using
(without the assert()s):
void vec_insert(vec* v, size_t index, void* value) {
if (v->_size == v->_cap) {
vec_grow(v);
}
for (size_t i = v->_size - 1; i >= index; --i) {
v->_values[i + 1] = v->_values[i];
if (!i) // if i == 0, --i will wrap!
break;
}
v->_values[index] = value;
v->_size++;
}
No, that's rather weird and unnecessarily overwrought way to guard
against this.
We can immediately apply the pattern I demonstrated above to this
and get
for (size_t i = v->_size - 1; i != index - 1; --i)
v->_values[i + 1] = v->_values[i];
Done. No need for an extra safeguard.
On 08/20/24 6:55 AM, Andrey Tarasevich wrote:
On 08/20/24 6:53 AM, Andrey Tarasevich wrote:
For example, iteration all the way to 0 can be idiomatically
implemented as
for (some_unsigned_type i = size; (some_unsigned_type) i != -1; --i)
...
Sorry, a typo. Was meant to be
for (some_unsigned_type i = size; i != (some_unsigned_type) -1; --i)
...
Of, crap. One more time
for (some_unsigned_type i = size - 1;
i != (some_unsigned_type) -1;
--i)
...
Should be good now :)
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
We can immediately apply the pattern I demonstrated above to this
and get
for (size_t i = v->_size - 1; i != index - 1; --i)
v->_values[i + 1] = v->_values[i];
Done. No need for an extra safeguard.
Better (please ignore cosmetic layout differences):
for( size_t i = v->_size; i > index; i-- ){
v->_values[i] = v->_values[i-1];
}
On 2024-08-22, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
We can immediately apply the pattern I demonstrated above to this
and get
for (size_t i = v->_size - 1; i != index - 1; --i)
v->_values[i + 1] = v->_values[i];
Done. No need for an extra safeguard.
Better (please ignore cosmetic layout differences):
for( size_t i = v->_size; i > index; i-- ){
v->_values[i] = v->_values[i-1];
}
Or even get rid of the for loop, and use memmove() :
memmove(v->_values + index + 1, v->_values + index,
(v->_size - index) * sizeof *v->_values);
Am 18.08.2024 um 10:03 schrieb Mark Summerfield:
void vec_insert(vec* v, size_t index, void* value) {
if (v->_size == v->_cap) {
vec_grow(v);
}
for (size_t i = v->_size - 1; i >= index; --i) {
v->_values[i + 1] = v->_values[i];
if (!i) // if i == 0, --i will wrap!
break;
}
v->_values[index] = value;
v->_size++;
}
Ultra-ugly, better use C++ and std::vector<>::emplace_back()/emplace().
Am 25.08.2024 um 06:30 schrieb Sjouke Burry:
Ultra stupid. This is a c newsgroup.
Not stupid because in C++ you've got a tenth of the work.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (2 / 14) |
Uptime: | 03:50:44 |
Calls: | 10,386 |
Calls today: | 1 |
Files: | 14,057 |
Messages: | 6,416,597 |