10 C99 tricks 2015-02-13

Here are ten interesting patterns and tricks I sometime use in my C code, some are well known, some not so much. I am sure all of them work with clang and gcc (after fixing the evetual typos). I didn't try with MSVC, but except for no 1 and 5, I think it should also work.

[edit]: Here is a link to the hacker news thread.

[edit]: Maybe the title is a bit misleading, this is not strictly about C99. Some items in the list are valid C89, and some others would not compile with -std=c99 -pedantic, and there are also gnu extensions. But pragmatically, all of those will compile with recent gcc and clang compiler.

0. Ternary operator without middle operand (gnu extension)

// Instead of x = x ? x : 10; // We can use the shorter form: x = x ?: 10; // Advantage: if x is an expression it // will be evaluated only once.

1. Unamed struct for smart vector type.

typedef union { struct { float x, y, z; }; struct { vec2_t xy; }; struct { float x_; vec2_t yz; }; float v[3]; } vec3_t; #define VEC3(x, y, z) { {x, y, z} } ... vec3_t vec = VEC3(1, 2, 3); // We can access the attributes in different ways. float x = vec.x; vec2_t xy = vec.xy; float z = vec.v[2];

2. IS_DEFINED macro

// As used in the linux kernel. // A macro that expands to 1 if a preprocessor value // was defined to 1, and 0 if it was not defined or // defined to an other value. #define IS_DEFINED(macro) IS_DEFINED_(macro) #define MACROTEST_1 , #define IS_DEFINED_(value) IS_DEFINED__(MACROTEST_##value) #define IS_DEFINED__(comma) IS_DEFINED___(comma 1, 0) #define IS_DEFINED___(_, v, ...) v // Can be used in preprocessor macros: #if IS_DEFINED(SOMETHING) ... #endif // Or even directly in the code. // Same effect but looks better. if (IS_DEFINED(SOMETHING)) { ... }

3. Convenience macro for OpenGL code

// Not really special, but so useful I thought // I'll put it here. Can also be used with other // libraries (OpenAL, OpenSLES, ...) #ifdef DEBUG # define GL(line) do { \ line; \ assert(glGetError() == GL_NO_ERROR); \ } while(0) #else # define GL(line) line #endif // Put GL around all your opengl calls: GL(glClear(GL_COLORS_MASK)); GL(pos_loc = glGetAttribLocation(prog, "pos"));

4. Array size macro

// Is there any C project that does not use it? #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) // Can be used like this: int a[] = {0, 4, 5, 6}; int n = ARRAY_SIZE(a); // n = 4 // Warning: does not work with array arguments to functions: int func(int a[]) { int nb = ARRAY_SIZE(a); // Would not work! }

5. Safe min macro (uses a gnu extension)

#define min(a, b) ({ \ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; \ })

6. Passing pointer to unnamed variables to function.

// A func that expects a pointer to three int values. void func(const int *arg); // Instead of using a local variable. int tmp[] = {10, 20, 30}; func(tmp); // We can write. func( (const int[]){10, 20, 30} ) // Can be useful with a helper macro. #define VEC(...) ((const int[]){__VA_ARGS__}) func(VEC(10, 20, 30)); // (Also works with struct or any other type).

7. Named initializer, with default values

// I use this one all the time when writing // video game. One of the reason why I // don't like to use C++. // Let say we have this struct struct obj { const char *name; float pos[2]; float color[4]; }; // We can write a macro like this one #define OBJ(_name, ...) \ (struct obj) { \ .name = _name, \ .color = {1, 1, 1, 1}, \ __VA_ARGS__ \ }; // Now we can use the macro to create new objects. // This one with color defaulted to {1, 1, 1, 1}. struct obj o1 = OBJ("o1", .pos = {0, 10}); // This one with pos defaulted to {0, 0}. struct obj o2 = OBJ("o2", .color = {1, 0, 0, 1});

8. X macros

// Define this once. #define SPRITES \ X(PLAYER, "atlas_0.png", {0, 0, 128, 128}) \ X(ENEMY0, "atlas_0.png", {128, 0, 128, 128}) \ X(ENEMY1, "atlas_2.png", {0, 0, 64, 64}) \ ... // Create an enum with all the sprites. emum { #define X(n, ...) SPR_##n, SPRITES #undef X } // Create an array with all the sprites values. struct { const char *atlas; int rect[4]; } sprites[] = { #define X(n, a, r) [SPR_##n] = {a, r}, SPRITES #undef X }; // Many other possibilities...

9. State machine helper using __LINE__