12 ms·
Note: this breaks if you want to pass struct literals: plot((myfoo){x,y}) Macros will take the struct literals as multiple parameters: plot( .ar
by ori_b 2y ago
Note: this breaks if you want to pass struct literals:
plot((myfoo){x,y})
Macros will take the struct literals as multiple parameters:
plot(
.arg0=(myfoo){x,
.arg1=y}
)
C macros are best left unused when possible.
- mananaysiempre 2y agoNope! In general, that can be a problem, but not for this specific technique: $ cpp -P void plot(float xlo, float xhi, float ylo, float yhi, float xinc, float yinc); struct plot_a { float xlo, xhi, ylo, yhi, xinc, yinc; }; static inline void plot_i(struct plot_a _a) { // inline thunk to allow arguments to go in registers plot(_a.xlo, _a.xhi, _a.ylo, _a.yho, _a.xinc, _a.yinc); } #define plot(...) (plot_i((struct plot_a){ __VA_ARGS__ })) plot((myfoo){x,y}) plot(.yinc=(myfoo){x,y}) ^D [...] (plot_i((struct plot_a){ (myfoo){x,y} })) (plot_i((struct plot_a){ .yinc=(myfoo){x,y} })) You could argue this is excessively clever, but when you need it, you really need it, so it could deserve known idiom status in the right situation.
- ori_b 2y ago> You could argue this is excessively clever, but when you need it, you really need it I've probably written millions of lines of C so far, and I don't think I have ever needed it.
- mananaysiempre 2y agoThe usual workarounds are a stateful API (e.g. Cairo, OpenGL, or Windows GDI), passing a structure explicitly (oodles of examples in Win32, e.g. RegisterClass or GetOpenFileName), or a twiddling an object that’s actually just a structure dressed up in accessor methods (IOpenFileDialog). There could be reasons to use one of those still (e.g. extensibility while keeping a compatible ABI, as in setsockopt, pthread_attr_*, or arguably posix_spawnattr_*). But sometimes you really do need a finite, well-known but just plain large number of parameters that mostly have reasonable defaults. Old-style 2D APIs to draw and/or stroke a shape (or even just a rectangle) are the classic example. Plotting libraries (in all languages) are also prone to this. It does seem like these situations are mostly endemic to specific application areas—graphics of all kinds first of all—but that doesn’t make them not exist. If you don’t want to use function-like macros for anything ever even if this particular one works, that’s a valid position. But it does work, it does solve a real problem, and it is less awkward at the use site than the alternatives.
- ori_b 2y agoWith large numbers of parameters, it's almost always more readable to use a config struct. Especially since often, you want to collect configuration from multiple sources, and incrementally initializing a struct that way is helpful.
- CamperBob2 2y agoC macros are best left unused when possible. Blame the committee for failing to specify an obvious and widely-demanded feature like named parameters. The only explanation is that the people in charge of the language don't write much code.
- szundi 2y agoOr they do and don’t want to learn stuff as “everything can be done the old way anyway”
- jimbob45 2y agoThere are a lot of syntactic sugar improvements the committee could make that they simply refuse to. Named parameters and function pointer syntax are compile-time fixes that would have zero runtime costs, yet it's 2024 and we've hardly budged from ANSI C.
- CamperBob2 2y agoExactly. I actually think default parameters are hazardous without named-parameter support. When they added one, IMO they should have added the other as well, so that you can specify exactly which non-default parameters you're passing.
- kevin_thibedeau 2y agoI think this is more an appeasement of the C++ committee because they don't like the order of evaluation to be ambiguous when constructors with side effects come into play. Witness how they completely gimped the primary utility of designated initializers with the requirement to have the fields in order.
- deleted 2y ago[deleted]
- deleted 2y ago[deleted]
- szundi 2y agoSay that to the Linux kernel or any embedded system
- ori_b 2y agoI've written both kernel code and embedded systems. It's easier to maintain the code when the preprocessor is avoided.