A recent comment on one of my F# articles got me thinking about this topic (thanks, Thomas! :-), so I thought I'd write a few posts on it. Next week is AU, and the week after that I'm attending a training class in Boston, so posts may be a little sparse over the coming weeks.

Metaprogramming  – according to the definition on Wikipedia – is the act of writing code that writes or manipulates other programs (or itself). But what is it really all about? The vast majority of programmers are actually metaprogramming without realizing it has such a fancy name.

To help understand metaprogramming, we're going to focus on two ways of categorizing the various types of metaprogramming activity. Metaprogramming is usually either static or dynamic and homogeneous or heterogeneous (there are other classifications, but we're not going to worry about those in this article).

  • Static = at compile-time
  • Dynamic = at runtime
  • Homogeneous = the same output language is used as on the input
  • Heterogeneous = a different output language is used to the input language

The most obvious form of metaprogramming is to create machine-code using a compiler (or even an interpreter) for a high-level language. This is a static, heterogeneous act of metaprogramming (although using an interpreter would presumably make this dynamic). Here are a few more interesting examples of metaprogramming I've used myself:

  • C++ templates or pre-processor macros to generate lower-level code at compile-time
    • Static (compile-time) and heterogeneous (the generation language is different from the output language)
  • Generation of a series of LISP expressions that are evaluated at runtime
    • Dynamic (runtime) and homogeneous (the generation and output languages are the same)
  • Programmatic creation (perhaps using LISP, C#, VB(A) or C++) of an AutoCAD script that is then executed
    • Dynamic and heterogeneous
  • Composing a SQL statement on-the-fly and using it to query a database
    • Dynamic and heterogeneous

The focus of this series of posts is on dynamic metaprogramming, which allows the modification of code at runtime (rather than compile-time). A further, somewhat more complex, example of dynamic metaprogramming is to redefine functions at runtime (something that's possible with LISP, for instance), which allows applications to evolve, such as when developing expert systems that "learn" over time.

LISP was really one of the early programming environments that enabled metaprogramming, primarily through its ability to evaluate expression using (eval) and to redefine functions at runtime using (defun). This has been immensely valuable to AutoLISP programmers over the years. When AutoLISP was first introduced it was purely an interpreted language, so dynamic metaprogramming was provided pretty much automatically. In order for metaprogramming to work after the introduction of Visual LISP (which is fundamentally a compiled environment, albeit to an intermediate language), a runtime component supporting dynamic compilation was needed and provided. Very little change was needed in AutoLISP code, although in some rare cases (defun-q) now needs to be used, if it's important to provide access to the internal representation of functions.

We'll see this is a common thread for dynamic metaprogramming: by definition you either need to be working in an interpreted environment or will need to have a runtime component available that supports some kind of compilation (probably JIT). Visual LISP provides this, as does VBA and .NET (via the CLR).

Back to AutoLISP: one very common activity is to interpret a string using (read) and then call (eval) on it. The string may have been stored in a drawing, a text file, an external database, or generated on-the-fly. For example:

Command: (eval (read "(* 5 (getvar \"ZOOMFACTOR\"))"))

300

VBA also has native support for dynamic metaprogramming via the Eval() function:

Eval "MsgBox ThisDrawing.Name"

VB6 doesn't have direct support for Eval(), but it seems you can make use of it either by embedding a Script Control or by calling across to the VBA runtime (Googling "VB6 Eval" returned a number of options). I don't know whether it's possible to evaluate and make use of AutoCAD-specific variables - such as ThisDrawing - when using these techniques, however.

Metaprogramming with .NET is not quite so automatic, but is altogether possible, as I'll show in my next post.

  1. Jose Guia at CadKicks.com Avatar
    Jose Guia at CadKicks.com

    OK, .. its peeked my curiosity, can't wait for part 2.

    You've been kicked (a good thing) - Trackback from CadKicks.com
    1

  2. >> (eval (read "(* 5 (getvar \"ZOOMFACTOR\"))"))

    Ugh... That's a bit Walker-esqe, along the same lines as the "autoload" business.

    This would be the 'pure' homogenous form:

    (eval (list '* 5 (list 'getvar "ZOOMFACTOR")))

  3. Tony,

    The point of using a string is to handle the case where we store, retrieve and evaluate code. Of course you can use the quoted version - which allows some level of dynamic behaviour - but that doesn't address the storage requirement.

    Regards,

    Kean

  4. Hi Kean.

    Regarding "The point of using a string is to handle the case where we store, retrieve and evaluate code".

    That's a precise description of what we do every time we save lisp code in a .LSP file, that we then load and execute 🙂

    The point to my comment is that LISP code, in its native form, takes the form of a LISP list, rather than a series of strings.

    Hence, lisp code that dynamically generates other LISP code, does so by constructing a list via the usual means of doing that. And that list can then be passed to (eval) to be executed as code.

    In case anyone isn't aware of it, the (vl-prin1-to-string) function is a cool and easy way to convert a LISP expression (whether it be data or a list that represents LISP code) into a string which can be persisted and retrieved in that form.

  5. hello
    I got one for you. I am running a VBA I created and its running in LDD 06--I am just adding xrefs to my drawings with VBA through a script--but after 25 minutes into the project--it errors out saying;

    Semantic error(s) in DCL file ade.dcl.
    See file acad.dec for details

    I can't find anyone to help--any ideas.
    thanks
    Paul

  6. Hi Paul,

    This appears to be specific to LDT (and possibly Map 3D), as it refers to ade.dcl. I don't have any experience with these products - you should try submitting the question via the ADN website (if you're an Autodesk Developer Network member) or posting to the AutoCAD Land Desktop Customization Discussion Group.

    Regards,

    Kean

Leave a Reply to Kean Walmsley Cancel reply

Your email address will not be published. Required fields are marked *