Microsoft’s T4 (Text Template Transformation Toolkit) framework is something every software engineer should have in their back pocket. It flies right along with the OOP principle of DRY (Don’t Repeat Yourself). If you’re writing DSL descriptors and then implementing their C# counterparts to be late bound, this shit is going to add YEARS to your life.
Check it out. The current project I’m working on is a very loosely coupled framework based on the concept of an Adaptive Object Model. The general idea is that you define your entire object model and class structure in a meta language (in our case it’s XML) and then let that drive how your objects are built and used.
The downside of (our particular implementation of) AOM is that you lose nearly all of the niceties that 4GL programmers are accustomed to having at their disposal to facilitate Rapid Application Development. Things like design-time intellisense.
In our AOM implementation we have a top-level object which has a collection of Commands. This command collection is populated at runtime and used by the programmer via indexing in to it with command keys. This results in code somewhat like the following:
Naturally you see the problem as you’re a developer using this kind of framework; “What commands can I call right here?”
The Commands collection is populated at runtime by objects inheriting from a common interface – in our case ICommand – which provides a simple call on it, Invoke(). The Invoke(), of course, does all the command-specific work however it needs to be done.
But hold up. In our case Commands also have Properties. So you are basically doing an Invoke() with some arguments. But you end up doing it like this:
Which, yeah, is cool and all but again how did you know Arg1 was there & available for setting? You didn’t. Chances are what you wrote before you started any of this was a Console or UT project that created MeObject() and looped though each command and spat out its name, then looped through each Property in the Command’s Properties collection and spat out that information as well. Only THEN were you able to know what you had at your disposal.
In our AOM, we use XML to define the collection of Commands and their Properties, which is then transformed and instantiated at runtime in to their corresponding C# counterparts. The benefit of this is that basically we can go in to the XML and comment out a command, and it’s no longer available off MeObject(). Pretty cool. Similarly, define a new command in the XML, create the C# implementation of it, and voila – it’s now available for use.
But if you read that carefully you see where, at run time of MeObject(), we know all the Commands we have at our disposal. Enter T4. T4 allows you to write code that writes code. Yup. In fact a while back when I was initially looking in to it I found an example of code writing code writing code. Inception much?
Knowing this, I had the idea to use T4 to generate intellisense for our AOM. Since our XML is embedded in the assemblies, so not modifiable after its been built in to our deliverable, it’s actually pretty sweet. Basically, my T4 actually creates a MeObject(), loops through the Commands, and spits something out like this:
Which gives me the awesomeness of seeing:
in my Visual Studio IDE. And since this T4 assembly is being processed after the assembly that MeObject() is defined in, I get to do this at build time to generate intellisense correct for each build, but not lose any of the compositionality of our AOM. Since ‘Key’ is a string constant, any existing code not using the T4’d stuff still works – no types were harmed in the making of this “shim layer”.
In next foray in to T4 on top of AOM will include using T4 to generate the classes from their meta-declaration in XML. Thereby removing that duplication of scaffolding to match the XML definition and letting the developer focus on getting it defined in the XML, then just writing the code that is executed. When we’ve finished that I’ll be sure to share what we come up with.
In the meantime, standby for a post containing ‘gotchyas’ that I encountered while working with T4 in an environment with build servers, UTs, etc that you’ll want to keep in mind if you adopt this strategy with anything you’re working on.