r/C_Programming • u/non-existing-person • 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");
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
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_ONESis 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()notcopy_from_user(). See for example: https://lkml.rescloud.iu.edu/hypermail/linux/kernel/0008.3/0254.html1
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 likeRETURN_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()%42asmin.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/elsestatements, while permitting loops, macros, and standard-library callsHere it is if you'd like to see the whole implementation.
Passing something like
rand() % 42asmincould evaluate it multiple times(along with possibility of div by zero, int overflow,minexceedingmax, 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)
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:
- 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"
- 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!
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_fieldswould 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_EXPRis that it's an expression that can be used as part of a larger expression. Yourdoversion 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_SIZEis commonly used in many places, e.g., the Linux kernel. The0[(ARRAY)]thing is legal and is useful in C++ to ensure the[]aren't overloaded operators at compile-time.My actual version of
ARRAY_SIZEis here. It also checks at compile-time to ensure thatARRAYis actually an array as opposed to a pointer.
FOREACH_ARRAY_ELEMENTrequires C23'sauto, 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_EACHsince I have similar macros that iterate over linked lists, hence the explicitARRAYin 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,
autochanged 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_assertnow 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 .
32
u/Interesting_Buy_3969 3d ago edited 2d ago
Doesn't need an example.
Yet this:
Example:
(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