I used to use all sorts of preprocessor tricks in my C code. I eventually made an effort to eliminated all preprocessor use in the code (except for #include, where there was no alternative).
The result was surprisingly pleasing. The code looked much nicer without all the #'s breaking up the indentation, and it made C look like a more modern language :-)
Honestly that's both poor naming and also abuse of macros combined with poor tooling (lack of syntax highlighting for macro usage for the reader). They do make it easier to shoot yourself in the foot, but they also let you do useful stuff that you simply couldn't do otherwise. The trouble is mostly teaching people to only use them for such purposes and not for anything else.
I think it’s hard to blame any macro system that operates at the source code level for this kind of thing since it’s basically impossible to prevent this type of abuse. The C preprocessor doesn’t enable it any more or less than others.
Where do you work that you get to work in C all day every day? I haven’t been able to do that in over 25 years. Sometimes I miss those days. Then you see one of these stupid macros!
GP means 'they do that unexpectedly.' In your C++ code, your function doesn't just up and decide to make changes to the parameters; `target` is a reference and therefore the caller should expect it to be modified. In GP's case, he's presumably complaining that the macro definition for `fbGetPixmapBitsData` gives no indication that the last three arguments will have their addresses taken to be passed elsewhere.
"In your C code, your macro doesn't just up and decide to make changes to the parameters. TARGET is a macro parameter, and therefore the caller should expect it to be modified."
Sorry, you don't hold water. A C++ function can be edited from a pure function to one with non-const, mutated ref parameter, and some (perhaps all) uses of that function in the program will still compile. That counts as "just up and decide to make changes to the parameters".
You have to read the definition to see what is going on, just like with a macro.
> macro definition for `fbGetPixmapBitsData` gives no indication that the last three arguments will have their addresses taken to be passed elsewhere.
If there is no indication, then it's not that macro itself that is arranging the mutation syntax. It must be expanding into something that looks innocent. E.g. we can have a macro over my C++ assign function above:
#define asn(x, y) assign(x, y)
Sure, the macro definition doesn't give a clue because it expands to something that looks like another function or macro call, which we then have to understand. The C++ function is the culprit that is mutating x, not the macro.
It's true that with macros, we can build up a labyrinth of expansions where such a thing can hide more easily; whereas if we use nothing but C++ functions, the one we are calling has to steal that address, not any of its delegates.
It's difficult to justify the macro aspect of the preprocessor, there are a thousand horror stories for every questionably good macro use-case. The only somewhat respectable macro definition usage I recall from my C++ days was type aliasing to support multi-platform build targets. But even that is hard to rationalize outside the standard libraries.
The result was surprisingly pleasing. The code looked much nicer without all the #'s breaking up the indentation, and it made C look like a more modern language :-)