Conman Laboratories

Better living through software …

Idiomatic Programming

Mark Grosberg

Language is probably human kind's most important invention. It governs how we think and what we can think about. The same is true for programming languages. The language we program in determines a lot about the overall design of our programs.

Most programmers are really passengers. The language drives their thinking instead of the other way arround. This is especially true for the imperative family of languages. For those unfamiliar with the term, an imperative language is based on things such as variables, loops, conditionals, evaluation, and procedures. Basically, the majority of programmers use imperative languages of some sort and never experience anything else.

Thankfully, imperative languages are not the only way to construct programs. Some languages provide very limited functionality, freeing programers from having to make so many choices. This is one of the main driving forces behind functional languages. Some functional languages attempt to drive a programmers thinking towards the right direction. The problem with this is that not every problem is solved with the same mentality. Other languages (not all of which are necessarily functional) are infinitely extensible (C++ does not count here, sorry).

Extensible languages allow the programmer to create a special purpose language oritented towards solving a particular problem. For example, given a task like expert system construction, a FORTH programmer might create a new word that compiles the expert system from an easier to write format that would be possible with a stack-based language.

Programmers who do this are telling the machine how to solve that class of problems instead of solving that one particular problem for the computer. This is a far more efficient (both in terms of programmer time and computational efficiency [if done right]) approach. Essentially, you should always “make the computer speak the language of the problem and not translate the problem into the language of the computer.”


Okay, great. I have convinced you that you are programming with blinders on because of your programming language (or environment for that matter). Changing programming languages can be an expensive, frustrating, and bug-inducing experience.

But you don't have to! In fact, most languages (such as C and Pascal+M4) have some sort of macro capability. Typically this capability isprovided by a preprocessor doing textual substitution. This means that macros do not quite integrate seamlessly with the language. But you live with what you've got.

While using macros can be dangerous (especially if you use them for the wrong purpose) they can also be a wonderful structuring tool. I tend to use macros for three main purposes:

  1. Enforcing consistency within complex code
  2. Changing the syntax of my programming language (which happens to be ANSI C) to match my problem
  3. Automatically coding something that would be tedious and error prone otherwise

I never use macros for the following reasons though:

The one exception to this is that (in C) I use macros for “open coding” (inlining) functions. This is essentially an efficiency concern that I solve using macros because the C language does not support inlining (sadly, but the compile-link mechanics of most C compilers prevent this).


Example 1: Enforcing consistency

When working on a large C++ telecommunication system (that was designed from the start to expand) our conventions call for every variable being accessed only through “Get” and “Set” methods. This is because in the future, simply updating a variable may require some associated action to take place. Along the same lines, getting the value of a variable may require that some associated action take place.

Furthermore, because of the approach we took to writing the “Get” and “Set” methods variable manipulation looks identical to function calling. Now instead of having to remeber two different concepts (assignment and function calling) you only have to apply one. Consistantly.

To perform this feat, our group used a macro that generated all of the repetitive code necessary to implement the “Get” and “Set” methods. This macro looked like this:

#define property(name, type)   :                             \
                                type m##name;                \
                                public:                      \
                                 type name()                 \
                                    { return m##name; }      \
                                 void name(type t__##name)   \
                                    { m##name = t__##name; }     

Although I don't like the overloading feature of C++ it is rather useful in this case. The way this macro works is it declares the variable with a lower-case m prefix (this was also required by our coding conventions). The macro also generates two access methods to get and set the value. The access methods have the same name as the variable. The way this macro can be used would be as follows:

class MyClass
{
  protected property(age, unsigned short);
  private   property(gpa, float);

  private:  // Other stuff can go here
};

Users of this class can use the properties like this:

...
MyClass  someInstance;

someInstance.age(10);                    // Set the age
printf("This %hu-old has a %f GPA.\n",
       someInstance.age(),               // Get the age
       someInstance.gpa());              // Get the GPA
...

Notice that now there is no thought (or distracting need to look up) what members of that class are functions and what members are variables. For all intensive purposes they are the same. Consistantly. And the code to make them such (which was once typed in by hand each time) is automatcally generated. This removes a great potential for bugs slipping in via typing errors.

Example 2: Changing the syntax

Some problems are just better solved using a special-purpose programming language. Any programmer who has ever written a compiler both by hand and with tools such as Lex and YACC knows that the automatic tools can make a compiler faster (in the case of Lex) and easier to write and modify (in the case of YACC).

Sadly, I am very reluctant to use Lex and YACC. Both of these tools are rather limited in their input mechanisms. As an added blow, the code generated by these tools is not re-enterant. Making anything other than the traditional compile file 1 completely, compile file 2 completely, … structure of a compiler difficult to implement.

These were exactly the problems I faced when coding the front end for my AMC compiler. In fact, the way AMC worked it completely prevented me from using Lex and YACC. My alternatives were to either:

or

Oddly enough I chose the latter. This turned out to be a very good design decision. Not only is my parser easily modified and fast but it is more robust than most because the macros I use automatically use a scheme to take care of reclaiming resources in the event of failure. Without me even having to think about it (because I sometimes forget, but the computer doesn't).

What I came up with was a set of macros that implemented some common actions such as:

These macros generate code that calls the parse/lexer runtime and manages the allocations that they make (automatically). The net effect is that if a syntax error occurs all of the resources allocated for the entire parse are automatically cleaned up and the stack is un-wound. What would have been very difficult to write became a few, very clear lines.


So, in short. Stop letting your programming language drive. Take control for the corners and turn on the cruise control for the highway.