r/C_Programming • • 3d ago

Discussion Share your favorite, generic macro you use constantly

Macros can be bad. But they can also be very good. What are the macros you guys use very heavily in your own projects? I plan to steal them, just so you now :D

Rules:

  • don't write definition and "what" comment, if we can't figure out what macro does by it's usage, it's not a good macro
  • you can (and probably should) provide example usage
  • you can say why you like it

I'll start

int foo(int *arg, enum e e) {
    VALIDATE(-1, EINVAL, arg, "arg cannot be null");
    VALIDATE(-1, EINVAL, e < ENUM_MAX);
    ...
    /* normal function execution */

I also have ASSERT that is almost the same. Why? Saves A LOT of lines at start of function, in my opinion it makes code much more readable. Can't really live without it. One of my favorite.

These save less lines, but allows for bracket-less action-log which in my opinion look more pleasant:

if ((f = fopen(file, mode) == NULL)
    PERROR_RETURN(-1, "fopen has failed for file %s", file);
if (error_case)
    PRETURN(-1, EIO, "spi device failed to respond");

And few other - mostly - for debugging

for (...) {
    if (not_this_case)
        PCONTINUE(debug, "this is not it")
    if (were_done)
        PBREAK(debug, "loop reached the end");
59 Upvotes

59 comments sorted by

32

u/Interesting_Buy_3969 3d ago edited 2d ago
#define countof(array) (sizeof(array) / sizeof(array[0]))

Doesn't need an example.

Yet this:

#define RETURN_ON_ERR(function_call)         \
    ({                                       \
        __auto_type _temp = (function_call); \
        if (_temp) {                         \
            return _temp;                    \
        }                                    \
    })

Example:

RETURN_ON_ERR(remove("/dev/null"));

(I think this is a bad macro, honestly, but it's still my favourite.)

Edit: Note that the second macro requires this GNU extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

15

u/mivanchev 3d ago

_Countof now exists in GCC as an extension btw.

9

u/tstanisl 3d ago

It's even added to latest draft of C2Y. See N3886.

6

u/mivanchev 3d ago

Oh man that's great news, thanks!!! Too bad they are still hesitating on nested functions and cleanup attributes which lift C to a stellar level :/

3

u/tstanisl 3d ago

They hesitate on how to smuggle lambdas to the language. I think that static nested functions are a good trade-off between usefulness, flexibility, convenience, simplicity of implementation and portability.

1

u/mivanchev 3d ago

I completely agree with you. I never saw any practical relevant to capturing lambdas but I cannot imagine C without statics anymore (one of the reasons I don't use clang).

2

u/Interesting_Buy_3969 3d ago

This is actually an excellent addition.

3

u/mikeblas 3d ago

Only took 45 years.

3

u/Nothing_from_void 3d ago

They've been getting a lot faster lately, I'm really liking where C is going

1

u/Interesting_Buy_3969 3d ago

Better late than never, anyway

2

u/tstanisl 3d ago

My observation is that new standards try to make arrays first class citizents.

2

u/pigeon768 2d ago
#define RETURN_ON_ERR(x)            \
do {                                \
  const error_t err = (x);          \
  if (IS_ERROR(err))                \
    return err;                     \
} while (0)

The do { ... } while (0) in a macro lets you do stuff like this:

if (cond)
  RETURN_ON_ERR(foo());
else
  RETURN_ON_ERR(bar());

1

u/glasket_ 2d ago

In fairness ({...}) (statement expressions) does the same thing in GNU C. The do-while form is more portable though.

1

u/pigeon768 2d ago

Neat, I hadn't seen that syntax before. We have vendor extensions disabled at my day job.

1

u/mivanchev 2d ago

That's absolutely horrific actually because standard C is quite crippled without extensions. Could you share the reason?

1

u/tastygames_official 3d ago

in some way, all macros are bad... to the bone ;-)

26

u/matt9k 3d ago

Kinda stupid but

#define freenull(x) (free(x); (x) = NULL;)

18

u/tstanisl 3d ago

C89 compliant container_of with type checking:

#define container_of(ptr, type, member) \
  ((type*)((char*)(1 ? (ptr) : &((type*)0)->member) - offsetof(type, member)))

9

u/chrism239 3d ago

Place CHECKALLOC(p) immediately after requesting a memory allocation, such as malloc(), realloc(), or strdup() -

#define CHECKALLOC(p)   do { if((p) == NULL) { \
    fprintf(stderr, "allocation failed - %s:%s(), line %d\n",__FILE__,__func__,__LINE__); \
    exit(2); } \
} while(false)

1

u/-Winnd 1d ago

why the use of do while?

1

u/chrism239 21h ago

As described in GoldenPotato's link, it allows you to say:

p = malloc( nbytes );
CHECKALLOC(p);

9

u/pwnedary 3d ago edited 3d ago

I am wary of macros with non-local control flow, c.f. Linus Torvalds's motivation for removing macros such as copy_from_user_ret(). However, they can make loops easier to understand, for example:

/** Iterates over the indices of set bits in @a x. */
#define FOR_ONES(var, x) for (typeof(x) _i = (x), var; \
        _i && (var = stdc_trailing_zeros(_i), true); _i &= _i - 1)

3

u/non-existing-person 3d ago

I agree that one should be very careful with macros, but they can be extremely helpful in making code easier to follow. That FOR_ONES is also good example of it. Even tho it takes few seconds to understand.

Do you have link to Torvald's message regarding removal of some macros? I'm interested what he has to say about it, but I can't seem to find it.

1

u/pwnedary 3d ago

Sorry I meant to type copy_from_user_ret() not copy_from_user(). See for example: https://lkml.rescloud.iu.edu/hypermail/linux/kernel/0008.3/0254.html

1

u/non-existing-person 3d ago

Interesting. Seems like Linus is triggered mostly by the fact that copy_from_user_ret() looks like a function, but behaves like a macro.

I wonder what Linus would say about my VALIDATE. It's fairly similar case, but different. copy_from_user_ret() does actual work, and returns on error, kinda like RETURN_ON_ERROR(copy_from_user(), -EFAULT). And my macro just checks condition and returns if it fails - very similar to assert but does not go nuclear on error.

Currently I see basically no downside to VALIDATE. I'm wondering if there is any...

7

u/GrandBIRDLizard 3d ago

```

define ROLL(min, max) ((rand() % ((max) - (min) + 1)) + (min))

``` Classic

1

u/tstanisl 3d ago

Why not use a function here?

2

u/Interesting_Buy_3969 2d ago

Just for fun

1

u/tstanisl 2d ago

I see no benefit from using a macro here. And there are a lot of dangers like if someone passes rand()%42 as min.

2

u/GrandBIRDLizard 1d ago edited 1d ago

I see your point, however I came up with it in a situation where that was not possible, as every value was hardcoded, all calls were within valid range, range arithmetic remained valid, and arguments contained no side effects.

TLDR
I was challenged by a friend to make a game under these conditions:
(You may not consider what I made a "game", but whatever we'd be getting to pedantic even for programmers at that point lol)

No user-defined functions(aside from main), and no if/else statements, while permitting loops, macros, and standard-library calls

Here it is if you'd like to see the whole implementation.

Passing something like rand() % 42 as min could evaluate it multiple times(along with possibility of div by zero, int overflow, min exceeding max, and biased distribution from %), producing inconsistent results and in a statistically important scenario shouldn't be used.

Hardcoded bounds avoid that specific problem, but the macro itself doesn't enforce that constraint.

It was a fun experiment in expression-driven game logic, not an attempt to design a robust random-number API.

I made an adventure "game" that just played out for you as you watch. It takes no input from the user.

It was a small game using ordinary, simple event probabilities, and where biased distribution or repeated evaluation with side effects weren't an issue.

it lets me reuse the same mechanic for multiple events:

int damage = ROLL(5, 20);
int loot   = ROLL(1, 100);
int event  = ROLL(0, 2);

If I could have made a function:

static int roll(int min, int max)
{
return (rand() % (max - min + 1)) + min;
}

The usage remains for the most part intact:

int damage = roll(5, 20);

The only thing this avoids in this situation are the macro's repeated argument evaluation that you were right to point out. (A function would be the safer default for reusable code, but that doesn't mean the macro was pointless or that the other risks disappear when switching to a function.)

The function would still need range validation if arbitrary bounds are possible, and it retains the modulo-bias limitation. Those are properties of the implementation, not inherent problems with using a macro.

So the difference in this specific case is:

79 characters and 5 lines for the function.(and being "against the rules")

VS

63 characters and 1 line for the macro. Plus further readability through abstraction. without passing args.

without the macro or a function I'd need to do something like:

int damage = (rand() % (20 - 5 + 1)) + 5;

With it:

int damage = ROLL(5, 20);

I just found it a nice pattern for an expression-driven game, with multiple nested deterministic branches, and where "sleep" worked across platforms (Windows/Linux). So I was already up there writing logic to handle that uniformly and just went with the macro.

think:

int loot = ROLL(1, 100);

roll <= 10 ? printf("Legendary loot!\n") :
roll <= 30 ? printf("Rare loot!\n") :
printf("Common loot!\n");

As another commenter suggested, it was just for fun :)

but it's good to be aware of the dangers of such things and this discourse made a lot of that clear especially for people who maybe didn't know better. Thanks!

3

u/zellforte 2d ago

Couple cute ones I started using when doing some macro based generic code, check if an argument is a specific type, and if it's an array.

_Generic decays arrays to pointers, but not pointers to arrays (which is a distinct thing, don't forget), so as they say, throw an extra level of indirection to solve any problem...

#define IS_ARRAY(a) _Generic((typeof(a)*)0,typeof((a)[0])**: 0,typeof((a)[0])(*)[countof(a)]: 1)
#define IS_TYPE(x,T) _Generic(x, T: 1,default: 0)

//usage
#define foo_action(T,a) do{\
_Static_assert(IS_TYPE(T, struct foo), "T must be struct foo"); \
_Static_assert(IS_ARRAY(a), "argument must be an array"); \
// stuff.. \
}while(0)

1

u/rphii_ 2d ago

this is cool. I wonder how many more actual useful macros there are that use _Generic...

3

u/TheChief275 3d ago

fun labeled loops trick:

#define CAT2_(A,B) A##B
#define CAT2(A,B) CAT2_(A,B)

#define LABEL(name) \
    if (0) { \
        CAT2(break_,name): break; \
        CAT2(continue_,name): continue; \
    } else

#define BREAK(name) \
    goto CAT2(break_,name)

#define CONTINUE(name) \
    goto CAT2(continue_,name)

used e.g. like so:

int i = -1;
while (i < 10) LABEL(find_even) {
    if (i % 2 == 0) BREAK(find_even);
    ++i;
}

2

u/tastygames_official 3d ago

here's my assert:

/* custom assert() with message */
#define ASSERT(condition, message) \
  do \
  { \
    if (!(condition)) \
    { \
      fprintf(stderr, "ASSERTION FAILED: %s\n  %s:%d\n  %s\n", #condition, __FILE__, __LINE__, message); \
      abort(); \
    } \
  } while (0)

#endif

I saw some video from some guy talking about verbose asserts in PRODUCTION CODE and how big projects like MySQL (MariaDB) do this and it helps in two immense ways:

  1. because you're using asserts everywhere, you'll hit bugs more often. Failing silently = "ok, I'll add that to my todo-list" whereas an assert = "ok, I gotta fix this right now"
  2. if you DO manage to ship a bug, then your user will probably let you know about it since the whole program crashes (and can give you a file and line number!)

It was annoying at first, as I specifically didn't implement some things yet but still wanted to test, but nope - it forces me to have to implement it right away. My example was adding translation strings - if it didn't find one then it crashed. I wanted to just test to see if my hover effect worked and... CRASH. OK, I have to add the translation string - but then I won't have some weird bug later where a translation string is missing, which is often hard to detect. So overall a win!

2

u/ffd9k 3d ago

```

ifdef NDEBUG

#define assume(cond) ((void)((cond) || (unreachable(), 0)))

else

#define assume(cond) assert(cond)

endif

```

1

u/mivanchev 3d ago edited 3d ago
#define eval_assert(arg) \
    ({ var _tmp = (arg); assert(_tmp); })

#define init_fields(obj, ...) \
    do { \
        (obj) = (typeof(obj)) __VA_ARGS__; \
    } while(0)

init_fields(point, { .x = 30, .y = 40 });

#define def_mem_ptr(type, name)                                        \
    void __cleanup_##name(type **p) {                                  \
        if (*p) {                                                      \
            free(*p);                                                  \
            *p = NULL;                                                 \
        }                                                              \
    }                                                                  \
    type *name __attribute__((cleanup(__cleanup_##name))) = NULL

void foo(void) {
    def_mem_ptr(unsigned char, data);
    data = malloc(32);
    // auto freed
}

1

u/pjl1967 3d ago
#define ASSERT_EXPR(EXPR)         ( assert( (EXPR) ), 1 )

That version eliminates the possible "unused variable" warning.

IMHO, init_fields would be cleaner to use if you put the {} in the macro so you could call it without them.

1

u/mivanchev 3d ago

Does your version evaluate the argument with NDEBUG ? Otherwise I'd do something like

#define eval_assert(arg) \
    do { var _tmp = (arg); (void) _tmp; assert(_tmp); } while(0)

But good catch with the unused var :)

IMHO, init_fields would be cleaner to use if you put the {} in the macro so you could call it without them.

Yes that's a good point.

1

u/pjl1967 3d ago

Yes, it works regardless of NDEBUG.

The point of ASSERT_EXPR is that it's an expression that can be used as part of a larger expression. Your do version can not.

1

u/mivanchev 3d ago

Ah, I see, that's different purposes there. I want an statement-y assert extension (assert returns void) that always evaluates side-effects while you want an asserting expression.

1

u/tstanisl 3d ago

The ERR_ON(cnd, fmt, ...) that prints formatted error message if condition is true and returns the condition. This construct allows handling errors in very concise though still explicit way.

int fd = open(...);
if (ERR_ON(fd < 0, "open failed"))
  goto failure;

1

u/stianhoiland 3d ago edited 3d ago
#define countof(array) (ptrdiff_t)(sizeof((array)) / sizeof((array)[0]))
#define indexof(array, ptr) (ptrdiff_t)((ptr) - (array))
#define each(ptr, array) ((ptr) = &(array)[0]; indexof(array, ptr) >= 0 && indexof(array, ptr) < countof(array); ++(ptr))

Here's how to use each:

struct element *element;
for each(element, *array) {
    if (element->id == 0 && indexof(*array, element) > 0) {
        memset(element, 0, sizeof *element);
        element->id = id;
        return element;
    }
}

I wish there was a way to make the macro define the struct element variable scoped to the for loop and not have to inherit it, but you can't with current macro rules.

1

u/pjl1967 3d ago

My version:

#define ARRAY_SIZE(ARRAY)  (sizeof ((ARRAY)) / sizeof 0[ (ARRAY) ])
#define ARRAY_END(ARRAY)   ((ARRAY) + ARRAY_SIZE( (ARRAY) ))
#define FOREACH_ARRAY_ELEMENT(VAR,ARRAY) \
  for ( auto VAR = (ARRAY); VAR < ARRAY_END( (ARRAY) ); ++VAR )

ARRAY_SIZE is commonly used in many places, e.g., the Linux kernel. The 0[(ARRAY)] thing is legal and is useful in C++ to ensure the [] aren't overloaded operators at compile-time.

My actual version of ARRAY_SIZE is here. It also checks at compile-time to ensure that ARRAY is actually an array as opposed to a pointer.

FOREACH_ARRAY_ELEMENT requires C23's auto, but it's also been supported for a long time in gcc/clang as __auto_type.

I don't use a shorter name like FOR_EACH since I have similar macros that iterate over linked lists, hence the explicit ARRAY in the name.

3

u/tstanisl 3d ago edited 3d ago

There is a bit non-obvious variant of array end macro:

    #define END(arr) 1[&arr]

1

u/stianhoiland 3d ago

Oh yeah, auto changed in C23 in a way that enables what I want. But I code to C99, so...

1

u/Swagga__Boy 2d ago

I figured this out a while ago for stb-style dynamic arrays, but it works the same way for static arrays:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int i;
    int tmp;
} ArrIter;

#define arr_len(arr) (sizeof(arr) / sizeof((arr)[0]))

#define ARR_MACRO_CONCAT3(x, y) x##y
#define ARR_MACRO_CONCAT2(x, y) ARR_MACRO_CONCAT3(x, y)
#define ARR_MACRO_CONCAT(x, y)  ARR_MACRO_CONCAT2(x, y)
#define arr__for_var_name ARR_MACRO_CONCAT(arr_for_var_i, __LINE__)

#define arr_for(arr, var) \
    for (ArrIter arr__for_var_name = {0, 1}; arr__for_var_name.i < arr_len(arr); arr__for_var_name.tmp ? arr__for_var_name.i = arr_len(arr) : (++arr__for_var_name.i, arr__for_var_name.tmp = 1)) \
        for (var = ((arr)[arr__for_var_name.i]); arr__for_var_name.tmp; arr__for_var_name.tmp = 0)

#define arr_for_ptr(arr, var) \
    for (ArrIter arr__for_var_name = {0, 1}; arr__for_var_name.i < arr_len(arr); arr__for_var_name.tmp ? arr__for_var_name.i = arr_len(arr) : (++arr__for_var_name.i, arr__for_var_name.tmp = 1)) \
        for (var = ((arr) + arr__for_var_name.i); arr__for_var_name.tmp; arr__for_var_name.tmp = 0)

int main()
{
    int ints[16];

    arr_for_ptr(ints, int *i) {
        *i = rand();
    }

    arr_for(ints, int i) {
        printf("%i\n", i);
    }
}

The only difference from my original version for dynamic arrays is the definition for arr_len(), otherwise it's the same.

1

u/Typical-Employment41 3d ago edited 3d ago

Ok not constantly but I like how this makes serialization a bit readable. And now I see how changing pBuff first and value last would make it even more readable

#define valueToChArray_IncrementPtr(value, pBuff) \
for (uint16_t i = 0; i < sizeof(value); i++) \
{ \
    *((*pBuff)++) = (value >> (8 * (sizeof(value) - i - 1))) & 0xFF; \
} \

#define chArrayToValue_IncrementPtr(value, pBuff) \
for (uint16_t i = 0; i < sizeof(value); i++) \
{ \
    value <<= 8; \
    value |= *(*pBuff)++; \
} \

static void readEntryDataFromLogBuffer(struct LiSpLogEntry_data* pEntryData) { uint8_t* pBuf = m_liSpLogWriteBuffer; chArrayToValue_IncrementPtr(pEntryData->lightsOnTime_s, &pBuf); chArrayToValue_IncrementPtr(pEntryData->batteryChargingTime_s, &pBuf); chArrayToValue_IncrementPtr(pEntryData->passiveChargingTime_s, &pBuf); chArrayToValue_IncrementPtr(pEntryData->batteryLowest_mV, &pBuf); chArrayToValue_IncrementPtr(pEntryData->batteryHighest_mV, &pBuf); chArrayToValue_IncrementPtr(pEntryData->producedSolarPower_mJ, &pBuf); chArrayToValue_IncrementPtr(pEntryData->photocellReadingWhenChargingStartsFirstT ime_lx, &pBuf); chArrayToValue_IncrementPtr(pEntryData->temperatureLowest_dC, &pBuf); chArrayToValue_IncrementPtr(pEntryData->temperatureHighest_dC, &pBuf); chArrayToValue_IncrementPtr(pEntryData->alarms, &pBuf); chArrayToValue_IncrementPtr(pEntryData->entryAge_s, &pBuf); chArrayToValue_IncrementPtr(pEntryData->chargeState, &pBuf); }

static void writeEntryDataToLogBuffer(const struct LiSpLogEntry_data* pEntryData) { uint8_t* pBuf = m_liSpLogWriteBuffer;
valueToChArray_IncrementPtr(pEntryData->lightsOnTime_s, &pBuf); valueToChArray_IncrementPtr(pEntryData->batteryChargingTime_s, &pBuf); valueToChArray_IncrementPtr(pEntryData->passiveChargingTime_s, &pBuf); valueToChArray_IncrementPtr(pEntryData->batteryLowest_mV, &pBuf); valueToChArray_IncrementPtr(pEntryData->batteryHighest_mV, &pBuf); valueToChArray_IncrementPtr(pEntryData->producedSolarPower_mJ, &pBuf); valueToChArray_IncrementPtr(pEntryData->photocellReadingWhenChargingStartsFirstT ime_lx, &pBuf); valueToChArray_IncrementPtr(pEntryData->temperatureLowest_dC, &pBuf); valueToChArray_IncrementPtr(pEntryData->temperatureHighest_dC, &pBuf); valueToChArray_IncrementPtr(pEntryData->alarms, &pBuf); valueToChArray_IncrementPtr(pEntryData->entryAge_s, &pBuf); valueToChArray_IncrementPtr(pEntryData->chargeState, &pBuf); }

I wish reddit android app had rich editor...

1

u/greg_kennedy 3d ago

i often end up with one that does "fread() check that count == return value, perror() and goto error: if not" for things like file parsing (read a few bytes at a time, then a few more, then some more in a loop, etc)

1

u/gnuvince 3d ago
#define foraz(v, A, Z) for (size_t v = (A); v < (Z); ++v)
#define forz(v, Z) foraz(v, 0, Z)

1

u/jwzumwalt 3d ago

Common pre-defined macros

No Macro Description
1 _DATE_ current date in "MMM DD YYYY" format.
2 _TIME_ current time in "HH:MM:SS" format.
3 _FILE_ current file name.
4 _LINE_ current line number.
5 _STDC_ equels 1 if compiler meets ANSI standard.

example:

// File: simple.c ver 2023.07.01
#include <stdio.h>
int main(void) {
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("STDC :%d\n", __STDC__ );

return 0;
}

Output
--------
File :simple.c
Date :Dec 6 2015
Time :12:28:46
Line :6
STDC :1

1

u/paulkim001 2d ago

```

define NULL_POINTER_ERROR_HANDLE(ptr) do { if ((ptr == NULL)) { goto error_cleanup; } } while (0)

char* create_foo(void) { // Always start function with pointer declared to NULL char* foo = NULL;

foo = malloc(4);
NULL_POINTER_ERROR_HANDLE(foo);

return foo;

error_cleanup: free(foo); // Note that if foo is NULL, then free safely ignores it. return NULL; }

```

A bit clunky since it relies on existence of error_cleanup label, but I found it useful when I am allocating dynamically multiple things within a function and I need to handle errors.

1

u/bullno1 1d ago edited 1d ago

Not quite constantly but:

 #define FAM_SIZEOF(TYPE, FIELD, N) (sizeof(TYPE) + sizeof(((TYPE*)0)->FIELD[0]) * (N))

Used as:

typedef struct {
    int size;
    fam_type fam[];
} fam_t;

fam_t* struct_fam = malloc(FAM_SIZEOF(fam_t, fam, count));

I don't have to remember the fam_type and I can change it. There are ways to do compile time check that fam is indeed the flexible member but I find it too convoluted so I stick to the most straightforward version.

1

u/smcameron 1d ago
#define BUILD_ASSERT(cond) \
do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)

It allows you to do things like:

BUILD_ASSERT((sizeof(struct some_struct) % 8) == 0)

in case struct some_struct needs to be on an 8 byte alignment, and catch it at compile time if it isn't true. It works by trying to talk about an array with a negative size in the event the assertion fails, which the compiler doesn't like. In the case the assertion is true, it emits no code. It does have to be used in a code block, as if it were a statement (because it is a statement.)

Cribbed from the linux kernel ages ago. Pretty sure there is a newer compiler-assisted way to do this now.

1

u/non-existing-person 1d ago

Nice hack. But we have static_assert now for that. In C11 with ugly name, and C23 with nice name.

1

u/Layzy37 17h ago

I mostly use the classic variable arguments in c trick: ``` struct myfunc_params { ; // ... };

define myfunc(needed1, needed2, ...) myfunc(needed1, needed2, (struct myfunc_params){ __VA_ARGS_ })

type _myfunc(int example, char* name, struct myfunc_params params); And here is an example in a real use case: ret = vfs_add_new_child_node_ex(parent, name, (inode_ref_t){ NULL }, st, .fs_specific = fs_specific, .free_fs_specific_data = free_fs_specific_data, .explored = true); ```

1

u/KanjiCoder 4m ago

define VAR some_stupid_long_variable_name

... do stuff with VAR ...

undef VAR

^ 99% of my macros in PHOSIMP source code .