5 ms·
Avoid magic. Do not use macros. Disagree. Use magic, especially macros, in ways such that your code becomes easier, not harder, to understand. A few examples
by cperciva 10y ago
Avoid magic. Do not use macros.
Disagree. Use magic, especially macros, in ways such that your code becomes easier, not harder, to understand.
A few examples from my own code:
1. My "elastic arrays" (https://github.com/Tarsnap/libcperciva/blob/master/datastruct/elasticarray.h https://github.com/Tarsnap/libcperciva/blob/master/datastruc... and .c) allow me to write
ELASTICARRAY_DECL(STRLIST, strlist, const char *);
and get a data structure STRLIST which contains an arbitrary number of strings and functions strlist_init, strlist_append, strlist_get, strlist_free, etc. for accessing the array. Compared to the non-macro approach of keeping track of the array size and resizing as needed, this makes code vastly simpler. (Of course, this sort of data structure is built into most non-C languages already.)
2. My "magic getopt" (https://github.com/Tarsnap/libcperciva/commit/53d00e5bd0478fcc89bc056d192a476fb89277a4 https://github.com/Tarsnap/libcperciva/commit/53d00e5bd0478f...) allows me to something which looks and behaves just like a standard UNIX getopt loop, except with support for --long options. Yes, the implementation is mildly insane (and needs to work around a bug in clang!), but it allows for code which is vastly simpler than other getopt-with-long-options alternatives.
3. My "cpu features support" framework (https://github.com/Tarsnap/libcperciva/blob/master/cpusupport/cpusupport.h https://github.com/Tarsnap/libcperciva/blob/master/cpusuppor...) makes use of both macros and some tricky edge cases of C object linkage rules, but makes it trivial for me to add support for new CPU features.
4. Soon to be released, the PARSENUM macro (WIP: https://github.com/Tarsnap/libcperciva/blob/parsenum-additions/util/parsenum.h https://github.com/Tarsnap/libcperciva/blob/parsenum-additio...) which allows me to write
PARSENUM(&n, "1234");
PARSENUM(&x, "123.456");
PARSENUM(&s, "123", 0, 1000);
where the first argument is a pointer to a variable of any integer or floating-point type to which is assigned the numeric value of the string in the second argument; for floating-point values and unsigned integers, the two-argument form range-checks the value against the bounds of the type, while the four-argument form range-checks against the provided bounds. (Basically, this is strtonum on steroids.)
In all of these cases, you will never need to understand how these macros work. Instead, you can simply treat them as language extensions which allow you to write cleaner and simpler code.
- Sir_Cmpwn 10y ago>1 I'm conflicted about this one. I choose not to try and emulate generics with macros because adding language features with macros is a terrible idea. On the other hand, I recognize the problem with void*. It's a matter for debate but I definitely fall on the "don't use macros for this" side. >2 This is a case where I would rather write more code than rely on magic. You have to have this awful hacky opaque implementation in exchange for a trivial improvement in ergonimics. No thanks. >3 I mean, just look at this code. Or better yet, have someone else look at it. This is totally unreadable and unmaintainable, all for a marginal ergnomics improvement. >4 Just use strtol or strtof. Do you really run into this that often? All of these are demonstrating exactly the problem I have with a lot of C authors. You build these esoteric systems that use heaps of unmaintainable, unreadable code to provide marginal gains elsewhere.
- makapuf 10y ago#define IT "I think" // :) >2 IT the swicth/cases are quicker/clearer to grasp than the one with BIGMACROS_X >4 IT you shouldn't underestimate the existing neural paths of knowledge of stdlib for C devs. those std functions are read / parsed almost intuitively, unlike cute macros, except for their creator. I'am ll for "cleaning" C and have a non-completely backwards dependent / improved standard, but I'd much prefer strtof than a custom macro. And those standard C functions are often accelerated to death by compilers (see: memcpy by example).
- cperciva 10y agoJust use strtol or strtof Which of these is more likely to have bugs? int i; if (PARSENUM(&i, s, 0, 1000)) err("Invalid input: %s", s); or int i; long l; // need a temporary long to avoid overflow char * ep; errno = 0; l = strtol(s, &ep, 0); if ((ep == s) || (*ep = '\0')) // make sure we parsed a number and don't have trailing garbage errno = EINVAL; if ((l < 0) || (l > 1000)) errno = ERANGE; if (errno) err("Invalid input: %s", s); i = l; You build these esoteric systems that use heaps of unmaintainable, unreadable code Not at all. The point is to have self-contained routines which Just Work in order to ensure that the rest of the code is easier to read and maintain.
- tobyhinloopen 10y agoAs a webdeveloper that likes to use C rarely for private things: I like that "elastic arrays" thing.
- cperciva 10y agoFeel free to use it. All of the libcperciva code is BSD licensed and should build and work on any C99-compliant system.
- _pmf_ 10y agoIt's worth keeping in mind that making stuff more maintainable for you does not necessarily make it more maintainable for others. It's nice if your successor can just use your abstractions, but if he needs to modify them ...
- cperciva 10y agoTrue. That's why I'm hoping more people will start using these.