3

I am passing an array of vertex indices in some GL code... each element is a GLushort

I want to terminate with a sentinel so as to avoid having to laboriously pass the array length each time alongside the array itself.

#define SENTINEL ( (GLushort) -1 ) // edit thanks to answers below
:
GLushort verts = {0, 0, 2, 1, 0, 0, SENTINEL};

I cannot use 0 to terminate as some of the elements have value 0

Can I use -1?

To my understanding this would wrap to the maximum integer GLushort can represent, which would be ideal.

But is this behaviour guaranteed in C?

(I cannot find a MAX_INT equivalent constant for this type, otherwise I would be using that)

P i
  • 29,020
  • 36
  • 159
  • 267

4 Answers4

5

If GLushort is indeed an unsigned type, then (GLushort)-1 is the maximum value for GLushort. The C standard guarantees that. So, you can safely use -1.

For example, C89 didn't have SIZE_MAX macro for the maximum value for size_t. It could be portably defined by the user as #define SIZE_MAX ((size_t)-1).

Whether this works as a sentinel value in your code depends on whether (GLushort)-1 is a valid, non-sentinel value in your code.

Community
  • 1
  • 1
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
1

I would create a global constant of value:

const GLushort GLushort_SENTINEL = (GLushort)(-1);

I think this is perfectly elegant as long as signed integers are represented using 2's complement.

I don't remember if thats guaranteed by the C standard, but it is virtually guaranteed for most CPU's (in my experience). Edit: Appparently this is guaranteed by the C standard....

S.C. Madsen
  • 5,100
  • 5
  • 32
  • 50
  • 2
    It is guaranteed by the C standard, even with machines that don't use two's complement representations. – Alok Singhal Jun 15 '11 at 12:52
  • 1
    @SCM: I think you missed the type there: 'const GLushort GLushort_SENTINEL = (GLushort)(-1);' – P i Jun 15 '11 at 23:42
1

GLushort is an UNSIGNED_SHORT type which is typedefed to unsigned short, and which, although C does not guarantee it, OpenGL assumes as a value with a 2^16-1 range (Chapter 4.3 of the specification). On practically every mainstream architecture, this somewhat dangerous assumption holds true, too (I'm not aware of one where unsigned short has a different size).

As such, you can use -1, but it is awkward because you will have a lot of casts and if you forget a cast for example in an if() statement, you can be lucky and get a compiler warning about "comparison can never be true", or you can be unlucky and the compiler will silently optimize the branch out, after which you spend days searching for the reason why your seemingly perfect code executes wrong. Or worse yet, it all works fine in debug builds and only bombs in release builds.

Therefore, using 0xffff as jv42 has advised is much preferrable, it avoids this pitfall alltogether.

Damon
  • 67,688
  • 20
  • 135
  • 185
  • 2
    The architectures for which `short` is not 2 bytes seem to fall into three general categories: old-ish supercomuters, archaic computers, and DSPs. – Dietrich Epp Jun 15 '11 at 13:03
0

If you want a named constant, you shouldn't use a const qualified variable as proposed in another answer. They are really not the same. Use either a macro (as others have said) or an enumeration type constant:

enum { GLushort_SENTINEL = -1; };

The standard guarantees that this always is an int (really another name of the constant -1) and that it always will translate into the max value of your unsigned type.

Edit: or you could have it

enum { GLushort_SENTINEL = (GLushort)-1; };

if you fear that on some architectures GLushort could be narrower than unsigned int.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • However, if you compare `(GLushort) x == GLushort_SENTINEL` you will invoke "undefined behavior" and GCC at least will always yield `false` / 0. – Dietrich Epp Jun 15 '11 at 14:23
  • @Dietrich, why should that be undefined behavior? – Jens Gustedt Jun 15 '11 at 18:14
  • I'm sorry, it usually won't invoke undefined behavior (although it will on systems where `short` and `int` have the same range). I had missed one of the nuances of integer promotions. However, the comparison will **ALWAYS** return `false` on systems where behavior is defined by the standard. (The comparison `(unsigned) x == (int) y` is always undefined, which is what I was originally thinking of.) – Dietrich Epp Jun 15 '11 at 19:35
  • The gist of it is that for `GLushort x`, `x == (GLushort)-1` will always work the way the asker wants, and `x == -1` will always be false (except on esoteric systems, where it is undefined). – Dietrich Epp Jun 15 '11 at 19:40
  • @Dietrich, why should `(unsigned) x == (int) y` be UB? The promotion rules apply here as well. If `unsigned` has more value bits than `signed` the `int` part is converted to `unsigned`, so this is by no means UB. If both have the same number of value bits (which is actually quite rare to non-existing) the `unsigned` part is converted to `signed`, fits very well in there, so everything is fine. – Jens Gustedt Jun 15 '11 at 21:15
  • 1
    Okay, I retract that, it is not undefined behavior. I keep forgetting that after integer promotions, the "usual arithmetic conversions" apply. Integer promotions (§6.3.1.1) only promote to `int` and `unsigned` from types of lesser rank, preferring `int` unless it cannot represent all possible values of the original type. Since `unsigned` and `int` always have the same rank (since `unsigned` is short for `unsigned int`, natch), the signed value is converted to unsigned (§6.3.1.8). This means `UINT_MAX == -1` but `USHRT_MAX != -1` except on esoteric platforms. Testing confirms this. – Dietrich Epp Jun 15 '11 at 22:53