vMAT uses a ruby DSL to do code generation for certain repetitive code patterns. For example, the way coercions between matrices of different types is handled (dynamically, at runtime1) currently requires implementing an Objective-C++dispatch method to copy to every numeric type from every numeric type. With 10 distinct numeric types that is 100 Objective-C++ methods that are slight variations of the same method template.
This is the problem that vMATCodeMonkey was designed to solve. Given the following specification in the file copyFromMethods.mk2:
123456789101112131415
# copyFromMethods.mk# vMAT## Created by Kaelin Colclasure on 4/23/13.# Copyright (c) 2013 Kaelin Colclasure. All rights reserved.require'vMATCodeMonkey'VMATCodeMonkey.new.coercionsdo|to,fm,to_t,fm_t|<<"EOS"- (void)_copy_#{to}_from_#{fm}:(vMAT_Array *)matrix;{ copyFrom(self, matrix, #{to_t}, #{fm_t});}EOSend
vMATCodeMonkey produces 100 methods are all slight variations on this one:
“That’s not very Impressive”, I hear you saying. Okay, let’s look at a more complex (but still highly repetitive) coding task: Parsing all those vMAT_API function’s options arrays.
Given this specification from clusterOptions.mk:
1234567891011121314
# clusterOptions.mk# vMAT## Created by Kaelin Colclasure on 4/26/13.# Copyright (c) 2013 Kaelin Colclasure. All rights reserved.require'vMATCodeMonkey'VMATCodeMonkey.new.options_processor<<EOS,:static"criterion:" arg: { choice => { "distance" => set(:useInconsistent, false), "inconsistent" => set(:useInconsistent, true) }}, default: "inconsistent""cutoff:" flag: set(:useCutoff, true), arg: vector(:double)"depth:" flag: set(:useInconsistent, true), arg: scalar(:index), default: 2"maxclust:" flag: set(:useCutoff, false), arg: vector(:index)EOS
As you can see, in this case the DSL specification looks nothing at all like the generated Objective-C code.
Both of these examples essentially entail boilerplate code. And it’s the kind of boilerplate that tends to get programmers in trouble; it’s clearly crying out for abstraction, but what kind of abstraction?
In a germinal version of vMAT (from even before embracing Objective-C++) I used the following idiom to implement the very first of the dispatch methods from the first example:
Clearly, this was disaster waiting to happen. Even with those ugly macros each copy & paste instantiation of this “template” needed to be carefully edited in four places. Unsurprisingly, it was exactly this exercise that pushed me over the barrier and into the realm of Objective-C++. But how much did templates really improve things?
Well, only by half, obviously. C++ allowed me to create the overloaded copyFrom function that is still used in the generated code. But there was still no way to template-ize those Objective-C selectors. And that’s where the first incarnation of vMATCodeMonkey entered the scene.
# vMATCodeMonkey.rb# vMAT## Created by Kaelin Colclasure on 4/23/13.# Copyright (c) 2013 Kaelin Colclasure. All rights reserved.classVMATCodeMonkeyMI_NUMERIC_TYPES=%w[ miINT8 miUINT8 miINT16 miUINT16 miINT32 miUINT32 miSINGLE miDOUBLE miINT64 miUINT64 ]MI_NUMERIC_TYPES.each{|to|MI_NUMERIC_TYPES.each{|fm|to_t=to[2,to.length-2]fm_t=fm[2,fm.length-2]print"- (void)_copy_#{to}_from_#{fm}:(vMAT_Array *)matrix;\n{\n"print" copyFrom(self, matrix, #{to_t}, #{fm_t});\n"print"}\n\n"}}end
Not a DSL at all; just a quick ruby script to complete what would otherwise be an annoying and error-prone task.3 But once you set foot down this path, it’s hard to stop.
In a (perhaps distant) future post, I’ll walk through a dissection of the current implementation of vMATCodeMonkey. Not that I think it’s a model implementation or anything, mind you. I’ve only been writing ruby code for a short time, and I certainly could not have completed this task without lots of assistance from the ruby community of StackOverflow. But I believe it’s a testament to the versatility of the language that even I, as a relative n00bie @ ruby was able to make this useful-if-highly-specialized tool. [Not that there weren’t a few speed bumps along the way. I’ll talk about those too.]
While C++ templates are a useful tool for handling repetitive code generation at compile-time, they can’t solve this particular vMAT challenge; there’s no notion of method overloading in Objective-C.↩
The extensions of the filenames of gists included in this article differ from the names used in vMAT’s code base. This was done to get more appropriate syntax highlighting.↩
I’d bet that’s how a lot of ruby DSLs got their start.↩