However, after using this style for many years, I’ve come to realize that it has many failings. One problem is explosion of change (example: a generated global header file). The extreme case is that a refactoring within a code generator requires the entire project to be recompiled: even though, by definition, the refactoring does not change the contents of any "source" file. This effect can be mitigated by clever makefiles; but it is not ideal.derived.c: derived.c.pl $(MODULES.pm) perl derived.c.pl > $@
Another issue, possibly a bigger problem, is that the use of code generators inhibit refactoring of the generated code. A common sentiment is “its like the output of a compiler, we don’t care what it looks like”, which quickly leads to: “it’s a mess: I don’t understand it, I’d better not touch it”; which eventually becomes “The code generator doesn’t support this feature: so lets not do it”: a code generator should not inhibit the creativity of its users.
I believe that there is a better approach: use the code generator as a test. Here’s how it works:
The first part of this is quite obvious: we create the generated code (as before); but we use it only as a test. This encourages people to think of the source code as real code: you make it readable, and you check it into the source control system.tests: source.c.diff source.c.diff: source.c.gen source.c $(DIFF) $^ > $@ || touch source.c.gen # rerun next time if fail source.c.gen: source.c.pl $(MODULES.pm) perl source.c.pl > $@ % make tests … % make tests DIFF=gvimdiff
The second part is a bit more subtle, but is where you leverage the power of the code generator. The code generator becomes a partner: suggesting changes, but not forcing you to make them. If the diff fails, then you can use your favorite merge tool to accept/reject individual changes. Or, if you’re feeling brave:
The point of all this is to free programmers from the tyranny of the code generator. It becomes possible to make a simple change to the C code, without having to fix the code generator immediately. You can still build the software, and run it. Sure, a test fails; but you can postpone fixing it for a few hours if you want. There is no worry that the code generator will suddenly come along and splat your changes.% make tests DIFF=cp
Another advantage of the DIFF approach is that it becomes easier to maintain the code generator. If you are refactoring the code generator, then the DIFF is a perfect test (it defines exactly the required behavior). If you change the C code, then you are able to easily practice test-first programming: the DIFF tells you what needs to change in the code generator. You don't have to rebuild the exe for every minor change!
You can get some of these benefits with clever makefiles using a generate-in-build flow; but I don’t think there are significant benefits for doing so. There are many benefits of separating the generation and build flows (coupling them only in the tests), and few disadvantages.
--Dave.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Code Generators as tests.
by lachoy (Parson) on Aug 19, 2002 at 20:48 UTC | |
by dpuu (Chaplain) on Aug 19, 2002 at 21:12 UTC |