I'm writing code for a thing that uses Lisp-like data structures (i.e. cons cells) internally. So it makes sense for the two field members of the cons struct to be called x->car
and x->cdr
.
As you would probably expect, I assign to those fields. Sometimes it's chained, as in x->cdr->cdr->car
(and yes, I do check to make sure the intermediary values are valid before doing this).
Because it's Lisp, however, I want to be able to use Lisp names for them, such as x->caddr
, but that would obviously not work because it's chained pointers, not one single pointer.
Currently I am using a bunch of preprocessor macros like so:
#define car(x) ((x)->car)
#define cdr(x) ((x)->cdr)
#define caar(x) car(car(x))
#define cadr(x) car(cdr(x))
#define cdar(x) cdr(car(x))
#define cddr(x) cdr(cdr(x))
#define caaar(x) car(caar(x))
// and so on all the way up to caaaaaar / cddddddr
This allows me to write stuff like caddr(x) = y
and have it work, because the macros expand into valid lvalues that can be assigned to.
However, the macros are not type checked. If I accidentally pass in a value that doesn't have car
and cdr
pointers, the error message is very hard to track down exactly what is the problem.
It also feels like a kludge too, as are most "preprocessor hacks".
I am looking for some way to express this that:
Any pointers?
(No pun intended)
answered by @273K's comment
inline object*& car(object* x) { return x->car; }
inline object*& cdr(object* x) { return x->cdr; }
inline object*& caar(object* x) { return car(car(x)); }
inline object*& cadr(object* x) { return car(cdr(x)); }
inline object*& cdar(object* x) { return cdr(car(x)); }
inline object*& cddr(object* x) { return cdr(cdr(x)); }
inline object*& caaar(object* x) { return car(caar(x)); }
// and so on
somehow, I forgot about C++ references