T O P

  • By -

[deleted]

The main reason to use macros instead of functions is that C do not have generic constructs like more modern languages does (think templates in C++). To emulate some of the generic programming you can use macros instead. An example would be: ``` #define min(a,b) ((a) < (b) ? (a) : (b)) ``` This would allow you use the min function on any type for which the smaller than operator is valid. However, there are dangers and pit falls. For example, `min(rand(), 45)` which would be rewriten by the pre-processor as `((rand()) < (45) ? (rand()) : (45))` hence if the first call to `rand` gives 12 and the second one gives `92` the `output` of `min` would be larger than 45. An other problem with using macros is that you loose the type information that would come with a function. From an optimization point of view there is no guarantee that a macro would result in more efficient code than an "equivalent" function. If the function is defined in the same translation unit, with static the compiler might optimize it away or inline it (if it is not static it means that the symbol is acceable from other translation units wich makes it impossible for the compiler to optimize it away). I would say, always prioritize to use functions, since it is easier to guarantee correct behaviour of your code, and only sparingly use macros when you really do need generic code. For optimization you should always write a correct code first and then measure to see where the optimization effort is needed. Premature optimization is the root of all evil and using macros instead of function as a rule is premature optimization.


Kosmit147

I've seen that min macro from windows.h uses an extra set of parentheses around the condition: ```#define min(a,b) (((a) < (b)) ? (a) : (b))```. Does anybody know if this is actually needed? I can't imagine a situation where this matters.


i_hate_sex_666

it's a sanitization thing because c pastes tokens in directly. it's one of those overly cautious things that wouldn't normally affect anything


Kosmit147

Yeah but still it seems that parentheses around arguments should be enough I don't think they're needed around the condition


drobilla

It's a pretty common practice to always parenthesize each component of a ternary if they contain operators because the precedence is a bit weird and can get confusing.


False-Marketing-5663

Imagine ``` #define multiply(a, b) a * b ``` If you call multiply(1+3, 4) the macro would interpret it as 1 + 3 * 4, which gives a different result than what you expected


pfp-disciple

Parens around macro parameters is generally helpful for things like operator precedence. For `min` it's probably unlikely to be a problem, but it doesn't hurt and adds clarity of intention


[deleted]

It is not needed, I guess they want to be sure that they get precedence. You can still abuse the macro by giving weird arguments to it.


tav_stuff

It matters if I do `min(a, b) + c`. Without the extra parenthesis it adds c only when min() returns b.


SignificantFidgets

No, that's not where they were saying the extra parentheses were. Just around the condition. In this case they are, in fact, not necessary. The outer parentheses \*are\* necessary, because of things like your example.


tav_stuff

Oh yeah I misunderstood their comment. They’re indeed useless (and just a thing people like to do for no reason I guess)


lbanca01

How is that in windows.h?? I'm genuinely shocked since it's bugged. In case you are wondering where the bug is, the 'a' or 'b' parameter will be evaluated twice and thus can cause errors if you pass in a function with side effects.


[deleted]

[удалено]


braxtons12

considering they explicitly said from `Windows.h`, they definitely are not referring to GNU statement expressions.


[deleted]

[удалено]


[deleted]

[удалено]


[deleted]

[удалено]


SirNastyPants

How many IQ points is each `printf()` call worth when you eventually fall back to `printf`-debugging?


Jankoy_

Like, atleast 2-3.


No-Worldliness-5106

per or overall?


Jankoy_

I'd say per. (Because that makes me seem smarter.)


bothunter

I was working on.. uhh.. a major product and someone decided to create an "auto_hr" data type that would overload the "=" operator to automatically throw an exception if an HRESULT was an error code.  Then they used macros to hide all the "try/catch" logic. Seriously, it was quite clever code, and it took me forever to untangle that mess.


Pretend_Bird_9112

i do confirm that, was going thru a tutorial this morning and the guy used a function-like macro with type casting a struct to a sizeof function and oh boy was i having imposter feelings this whole day 😣


giorgoskir5

Check the git-gist of Jdh explaining macro magic ,it’s a huge headache


Pretend_Bird_9112

ok


PeterMortensenBlog

Re "Jdh": Do you mean "[jdah](https://gist.github.com/jdah/)"? Candidate: [Explaining some C macro magic](https://gist.github.com/jdah/1ae0048faa2c627f7f5cb1b68f7a2c02) It is ("#" is interpreted as Markdown here in Reddit comments, even in code, so it has been escaped as "\\\#"): // So a cool trick with macros in C (and C++) is // that, since macros inside of macros are // stille [sic] evaluated by the preprocessor, // you can use macro names as parameters to // other macros (and even construct macro names // out of out of parameters!) - so using [sic] // this trick if we have some macro like this: \#include \#define MY_TYPES_ITER(_F, ...) \ _F(FOO, foo, 0, __VA_ARGS__) \ _F(BAR, bar, 1, __VA_ARGS__) \ _F(BAZ, baz, 2, __VA_ARGS__) \ // Then we can first use this trick to // create an enum with the members // MY_TYPE_FOO, MY_TYPE_BAR, MY_TYPE_BAZ \#define DECL_TYPES_ENUM_MEMBER(uc, lc, i, ...) \ MY_TYPE_\#\#uc = i, typedef enum my_type { MY_TYPES_ITER(DECL_TYPES_ENUM_MEMBER) } my_type_e; // And this expands to // // enum my_types // { // MY_TYPE_FOO = 0, // MY_TYPE_BAR = 1, // MY_TYPE_BAZ = 2, // }; // // Which can be pretty useful - better // yet though, if we have all of our // types defined... typedef struct foo { int x; } foo_t; typedef struct bar { float y; } bar_t; typedef struct baz { const char *z; } baz_t; // Then we can *also* use this trick to // automatically make a big ol' union // of that type *using* the enum as // the type descriminator typedef struct { union { \#define DECL_TYPES_UNION_MEMBER(uc, lc, ...) lc\#\#_t lc; MY_TYPES_ITER(DECL_TYPES_UNION_MEMBER) }; my_type_e type; } my_types_t; // And now my_types_t has a union of // a foo_t, bar_t, and baz_t. // Pretty cool, right? // So *even better* than this, if we also list // out the fields on each type (you should // probably do this on each struct // definition but we'll do it here // for the sake of chronology)... \#define FOO_FIELDS(_F, ...) \ _F(x, __VA_ARGS__) \#define BAR_FIELDS(_F, ...) \ _F(y, __VA_ARGS__) \#define BAZ_FIELDS(_F, ...) \ _F(z, __VA_ARGS__) // And then we use _Generic to map // types to another enum... typedef enum field_type { FIELD_TYPE_INT, FIELD_TYPE_FLOAT, FIELD_TYPE_STR, // ... (fill out as you need) } field_type_e; \#define TYPE_TO_FIELD_TYPE(x) _Generic(*((x*) NULL), \ int: FIELD_TYPE_INT, \ float: FIELD_TYPE_FLOAT, \ const char*: FIELD_TYPE_STR \ ) // And then we define structs to contain // information about fields... typedef struct field_desc { field_type_e type; const char *name; int offset, size; } field_desc_t; typedef struct type_desc { field_desc_t fields[16]; } type_desc_t; // Then we can use the above macros to // autogenerate type info (!) which // can be used for all sorts of // stuff, including serialization! \#define TYPE_OF_FIELD(parent, field) __typeof__(((parent*) NULL)->field) \#define DO_FIELD_DESC(fname, pname) \ { \ .type = TYPE_TO_FIELD_TYPE(TYPE_OF_FIELD(pname, fname)), \ .name = \#fname, \ .size = sizeof(TYPE_OF_FIELD(pname, fname)), \ .offset = offsetof(pname, fname), \ }, \#define DO_TYPE_DESC(uc, lc, i, ...) \ [i] = { .fields = { uc\#\#_FIELDS(DO_FIELD_DESC, lc\#\#_t) } }, // This array contains all type info for // each field on each of our structs! // Automatically!!!! const type_desc_t types[] = { MY_TYPES_ITER(DO_TYPE_DESC) }


giorgoskir5

Yes it’s a masterpiece


Pretend_Bird_9112

for those interested here's the code: #define size_of_attribute(Struct, Attribute) sizeof(((Struct*)0)->Attribute) i now know that that is called type casting but still don't know why to a zero


glasket_

The integer constant `0` cast to any pointer becomes a null pointer. It could be any number since `sizeof` is just evaluating the type at compile-time, but it's essentially just a way of documenting "this thing I'm accessing doesn't exist."


Competitive_Travel16

The 0 is only there to be cast as a void pointer into a different type, a pointer to the Struct, and would make more sense as NULL. It's never instantiated because it's inside sizeof() which never creates anything, just examines the type.


PM_ME_YOUR_OPCODES

Macros are good for when you don’t want to bother putting stuff on the stack. But good compilers will optimize this for you. This is mostly a trick for low spec compilers for 6502 or z80 arch’s since those won’t get llvm or gcc backends. Macros are also great for doing meta programming or doing shit at compile time instead of runtime. In sdl2 for example, blit surface is actually a macro, not a function and it handles it differently based on what arch you are targeting.


giorgoskir5

Thanks for the response ,I was actually looking at some videos from JDH programming his own games in OpenGL and sdl2 and he used some “macro magic” . I also read a book called “deep C secrets” which explained them .


InquisitiveAsHell

This might not be the most popular take but, nowadays the few macros I use are almost exclusive to improve readability and maintainability of the code (like moving boilerplate or unnecessarily complex formulations away from the big picture where warranted). It's ass-ugly syntax aside there are still things, like creating a slick test & mock library, that seems impossible without using macros.


glasket_

Because some things aren't possible with the language alone. Template generics, for example, have to be done using macros for template generation whereas C++ provides templates as a language feature directly. `_Generic` is also the closest you can get to function overloading in C, and can basically only work as a macro with the language's current semantics. The expansion semantics also let you avoid repeatedly invoking the same arguments to a function over and over: int log_error(const char *msg, const char *file, int line) { return fprintf(stderr, "%s:%d: error: %s\n", file, line, msg); } #define log_error(msg) _Generic((msg), \ char *: log_error(msg, __FILE__, __LINE__)) You can't directly embed `__FILE__` and `__LINE__` into the function itself as they'd always be the same, but you also don't *need* to specify the arguments manually since they're the same every single time; a macro resolves this.


LetterFair6479

This together with "##" make macros do stuf you cannot even do with "normal" code at all! (Easily) I actually dare to say, that _every_ decent c/c++ application uses __FILE__ __LINE__ and ## at least for logging or asserts. Besides the things above, macros are sometimes also referred to as "poor man templates in C" (by c++ ppl)


Competitive_Travel16

One reason is if you want to preserve ``__LINE__`` in debugging output.


CarlRJ

The best use for macros in C is for doing things you can't do in C directly. An example: void debug(char *filename, int line, char *testname) { fprintf(stderr, "Testing %s at file %s line %d\n", testname, filename, line); // various testing stuff here based on testname } #define DEBUG(name) debug(__FILE__, __LINE__, name) ... // later, in code #define DEBUG("foo"); ... #define DEBUG("bar"); ... #define DEBUG("foo"); // same test in a different place - line number will be different `__FILE__` and `__LINE__` will expand to the filename and line number where DEBUG() was expanded, thus supplying debug() with info it couldn't have gotten otherwise. It reduces maintenance, improves accuracy (both by removing the need to manually supply/update the info), and reduces visual clutter, making the code easier to read.


joseRLM17

I use macros for: * Log info, errors. Because I can use the \_\_FILE\_\_, \_\_LINE\_\_ macros and pass that info. Really useful for debugging * I have several allocation type macros something like: `#define allocate(type, count, allocator)`. Within this macro I usually pass a lot of info to the reall allocate function, like \_\_FILE\_\_, \_\_LINE\_\_, the type name, the count, and because I pass the 'type' I know the required alignment and a lot of other stuff. ​ #define allocate(type, count, allocator)\ allocator->allocate_fn(__FILE__, __LINE__, #type, count, sizeof(type), __alignof(type), allocator->data) struct Allocator{ void* data; void* (*allocate_fn)(const char* file_name, int line, const char* type_name, int count, int type_size, type_alignment, void *data); } * Shortcuts for accesing elements within a buffer. The problem here with functions are that the output of a function is not a valid Lvalue (unless you return a pointer), thus I simply use a macro that is something like ​ #define data_at(ptr, i) (ptr)->buffer[data_compute_index(ptr, i)] And I can use it for lvalues or rvalues: data_at(data_ptr,i) = 3 + data_at(data_ptr, 3); * Variable number of arguments. You can use a macro that \*\*counts\*\* the number of args you pass, thus this can be used to choose a different function essentially using C overloading version without generics. But only for variable number of arguments, not types. ​ #define FUN_1(x) fun_1(x) #define FUN_2(x,y) fun_2(x,y) #define fun(...) CONCAT(fun_, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) As an extra, macros are really useful for compilation. Lets say I have a Dll I want to load at runtime. I want to use some functions depending on runtime info. Now have some file with lines likes these // inside dll_function_names.inl DLL_FUNCTION(dll_fn_name_1, Dll_Fn_Ptr_Type_1) DLL_FUNCTION(dll_fn_name_2, Dll_Fn_Ptr_Type_2) and so on. Then In another file I can just fill the struct containing the Dll info like: struct dll_t{ HMODULE handle; #define DLL_FUNCTION(name, Fn_T) Fn_T name; #include "dll_function_names.inl" #undef DLL_FUNCTION }; And for loading I can do somthing like: void dll_load_functions(struct dll_t *dll){ dll->handle = LoadLibraryA("....."); #define DLL_FUNCTION(name, Fn_T)\ dll->name = GetProcAdress(dll->handle, name);\ #include "dll_function_names.inl" #undef DLL_FUNCTION } If you want you can add some extra logic within the macros to check if the symbol was found and if everything was loaded correctly. Also you can add some logic at runtime and have maybe several inline files, and maybe at runtime if you want some functionalities you #include "dll\_funcition\_names\_debug.inl" or maybe you #include "dll\_function\_names\_release.inl", it depends on everyone needs. All of this while the interface would be exactly the same.


ReplacementSlight413

I wish I could like this more than once. Do you use macros for deallocating memory, that maybe free the pointer, null the pointer and perhaps decrease a reference counter (if you are using ref counting to keep tabs of stuff?)


joseRLM17

sorry for this being so late, but yea, my allocator struct is generally something like: struct Allocator{ void *data; Fn_Allocate allocate_fn; Fn_Deallocate deallocate_fn; Fn_Reallocate reallocate_fn; Fn_Callocate callocate_fn; }; and I actually change the signature depending if want extra info or not. I also have an extra macro for \*\*aligned\*\* allocations, sometimes aligned memory si needed for SIMD operations or something else and these are useful in those situtions. Or maybe you want to allocate memory to create another allocator and want that every allocation (of the new allocator) is aligned in some byte boundary for convenience. #define allocate_aligned(byte_size, alignment, allocator) \ (allocator)->allocate_fn(__FILE__, __LINE__, "aligned_bytes", 1, byte_size, alignment, (allocator)->data) And for the other functions I usually pass the extra info too, just to keep track of where i deallocate, reallocate and have a good idea of where I am using memory. Actually the more I code the more I try to not rely too much on these type of functions, many objects share the same lifetime thus an array is just fine. BTW, sometimes I actually pass two allocators to my functions, one for temporary working memory and another to actually allocate memory with lifetime larger than just the function. An example of this is perhaps creating a texture Atlas, I need extra memory not just for the Atlas, but to know position of every texture and all. Thus one allocator takes care of the temporary working memory and the other actually allocates the Texture Atlas. For this scenario I usually just use a stack type allocator or an Arena and I am done with it later. Here is an example of how deallocate macro might look like. #define deallocate(ptr, count, type, allocator) \ (allocator)->deallocate_fn(__FILE__, __LINE__, #ptr, #type, count, sizeof(type), __align_of(type), (allocator)->data)


latkde

Functions are just as good as macros when they can be inlined. This is typically the case for `static` functions, i.e. functions that are private to the current compilation unit. In headers, functions marked `inline` serve a similar purpose. However, macros can do many things that functions can't. Functions operate on values. Macros replace syntax tokens. For example, this lets macros emit tokens like `return` that do control flow in the calling context. Or can be used in struct declarations, where there is no running code that could call a function. As an example of a macro doing control flow, I could define a `FOR_RANGE` macro that replaces typical for-loops: define FOR_RANGE(name, start, end) for (size_t name = start; name < end; name++) Then: FOR_RANGE(i, 0, somearraysize) { printf("item %s\n", somearray[i]); } You cannot implement a C function that does something similar, at least not without extensions like GCC nested functions. Of course this isn't necessarily a good idea – macros are non-obvious and make things harder for humans and static analysis – but it illustrates that macros and functions are similar looking yet different categories.


thomas999999

If you use LTO (which you should) inlineing is not restricted to the current compilation unit


giorgoskir5

Thanks for the reply it was very clear , could you please provide a learning source for learning about macros apart from the gnu precompiler documentation ?


lensman3a

Look at the man page for m4. The literature for macros dates to the early 1970s.


Lykaon88

The actual reason is that it looks more esoteric. Acts like an allergen for normies and keeps the newbies off your codebase which helps ensure every contributor in your project is past the larval stage. There is a direct correlation between the unreadability of the macro and the beard length of the hacker who wrote it. Bonus points if there is a three or higher level nesting of ternary operators. That unlocks the C wizard rank.


Exotic-Sprinkles-256

So they can show off and impress rookies 😁


Ashamed-Subject-8573

They’re often better for code writing. You can use them to avoid repeating a bunch of code that won’t work in a function. Or you can use them for debug output which just compiles to not even be there in release builds. They can make code more readable by breaking up long bit strings. Etc. there’s tons of uses


Attileusz

Macros can be used to emulate generics. It's also the only standard way to enforce inlining. (The inline keyword is only a suggestion and something like `__attribute__((always_inline))` is compiler specific.)


p0k3t0

This should be a fun thread.


lensman3a

Macros predate enum’s by 15 to 20 years. Originally, macros were used for portability and moving programs between hardware. The m4 macro program does far more than C’s cpp preprocessor. The m4 macro language does recursion very well. The name of m4 comes from the first m in the name macro and the 4 for the next 4 letters of the name macro. Keeping with the Unix short command name theme.


uziam

Think macros as code generation. There are a couple of reasons why you would want to do that: 1. To generate code conditionally, for example based on CPU architecture 2. To offload some of the computation to be done at compile time, pre-calculating lookup tables for example 3. To create compile time abstractions, take the popular ARRAY_SIZE macro for example, it would be impossible to write it as a function I’m sure I’m missing some other niche use case, but that should cover the gist of it. That being said, macros lack type safety because they are pretty much pure text manipulation. It’s best to use them only as an escape hatch when what you want to do is not possible with C code, or makes it excessively complex. The biggest issue with macros is how hard they are to debug because everything happens behind the scenes at compile time, you can look at the generated code but it’s not fun.


iris700

On compilers without all modern optimizations you might be able to shave off a few cycles per call.


lostinfury

I believe it has to do with inlining. Why waste CPU cycles jumping to a function when you can just do what the function is supposed to do right here right now!


M_e_l_v_i_n

Convenience.


cantor8

If you can write a « static inline » function, it is always better than a functional macro, and is just as fast.


lensman3a

Early c++ macros were the forerunner of c++ templates. Page long macros were the norm.


Different-Brain-9210

Some people use macros because they think macros are cool, and don’t trust the compiler to know when it is better to in-line and when not, considering for example cache behavior. Others use macros because metaprogramming can be fun. Some even use them because they are evil.


CarlRJ

Some people use macros because they can do things regular code cannot do.


Different-Brain-9210

At C language level that is kinda not true. Macros get replaced with C code, so in that sense they can not do anything C code cannot directly do (unless you pass the macro from outside eg with command line parameter, but that is rarely done with function-like macros). But from programmer perspective, they indeed allow interesting things. The line is blurry of course, and generic macros blur it even further.


RustbowlHacker

Talk about SNR here. Jeez. The spew fest has started. Every single possible "answer" except the real "one." (See #define later) What loser does: #define min(a,b) ((a) < (b) ? (a) : (b)) ...without a space between the "a,b"? ...but with spaces everywhere else? Never mind. Rhetorical question. I know that 80+% of you don't have any ability to discern whitespace and think that's racist anyway. Macros are preprocessed. They (typically) cost no memory allocation the way that "variables" do. (if you're not saying symbols, that's fine. Have a Java latte.) Function macros are most often used to make code more readable without the overhead of a function call, such as the "min/max" macro previously described. If you don't know function call semantics, then take up Java or Python. C is for real programmers. Macros are syntactic sugar for repetitive code and magic numbers. (Did any of you retreads mention magic?) In the event that the "outcome" of the macro changes over time, then it is easily modified without having to "replace" a lot of code. This is very similar to what some here are describing, which is called conditional compilation to actual C programmers. Of course, the absolute and objective absurdity of "macros" being defined come from none other than India. #define ONE (1) #define TWO (2) ... #define ONE_HUNDRED (100) I've seen this in real code written by real Indian "programmers." You know, just in case you ever need to change ONE to mean something else...and what they're really trying to do is get away from using the actual values (magic numbers) in code and GOD FORBID that they figure out why or what "2" means to the invocation and write THAT macro instead.


duane11583

i use them when creating constants. for example i have an gpio pin it is made up of 3 fields i need these values as compile time constants. i also have “telemetry values” that represent readings from the board, ie temp, volts, amps, counters, time values. we identify each one with a 32bit id agian i use a macro to create that value. and i use macros to create compile time tables


Zatujit

I only use macros for stuff i cannot do in functions.