The Art of Metaprogramming (2005)
ibm.com
ibm.com
Here are links to the generators for those projects:
https://github.com/libguestfs/libnbd/blob/master/generator/g...
- This is a small C library. It uses a small-ish generator to generate a state machine, Python bindings, OCaml bindings and (patch coming soon) Rust bindings.
https://github.com/libguestfs/libguestfs/tree/master/generat...
- This is a very large project mostly written in C, using a generator to generate bindings in multiple programming languages, also RPC boilerplate, structures, configuration code and much more. This generator is very large having evolved with the project over 10+ years.
Edit: Some rules for effectively generating code:
(1) The generated code must look as far as is reasonable like it was hand written. This is because you'll be debugging it. We also use #line directives where possible.
(2) chmod -r the generated files so they cannot be edited by accident. Also add a comment at the top pointing to the generator so that developers who have no idea how it works can immediately find the right place.
(3) We add the generated files to the tarball (to allow end users to build without needing OCaml) but keep them out of git (to avoid duplication in commits).
(4) Maximum generation! If some piece of information is written twice, it should probably be written once (in the generator) and the output should go to those two places. Similarly we only have one generator in the project so no one needs to read the Makefiles to find out how a file is generated.
Also slightly off-topic, but in SQL you can insert many tuples in one statement like this
insert into people (name) values ('Tony'), ('Bob'), ('Mary'); $ cat multiple-insert.sql
create table users (first, last);
insert into users values ('fred','bloggs'), ('mary', ‘contrary');
select * from users;
$ sqlite3 < multiple-insert.sql
fred|bloggs
mary|contraryIf willing to live on the edge, one can do some serious code generating in the SELECT clause of an SQL statement.
Mwahahahaha.
Also people should look into Common Lisp (Seibel book, and AMOP for more ceremonious stuff)
https://docs.oracle.com/cd/E11882_01/appdev.112/e10825/toc.h...
I've worked on a legacy app written with these tools. It's not the most awful thing I've ever had to use. But there's a lot of magic in those macros, and you have to code carefully to avoid introducing action-at-a-distance bugs, esp. w.r.t. error handling.
For languages like Go and Rust, this is partly solved by putting tooling into the language and standardizing the build system. But even for languages that have build systems, there’s reasons to use other build systems.
As far as I know, very few build systems used in the open world have readily available integration with tooling. However, I think this is temporary. As an example, Bazel and Go should play well together in the near future:
https://github.com/bazelbuild/rules_go/issues/512
Hopefully, other languages will make mechanisms for code intelligence to integrate with build systems. This is especially important if you want a good developer experience across multiple programming languages.
Perhaps the more scalable (in terms of supporting many languages x many build tools) alternative would actually be an inverse relation, a future language server architecture where the build tool orchestrates the language servers. Who knows.