XTRAN Example — Impose Preprocessor Macro on Existing Code


Introduction

Multiplying an integer by a power of 2 can be done in either of two ways:

While the left shift is more efficient at run-time than the multiplication, it is less obvious when reading the code what the programmer's intent was.

Also, while it's possible that a compiler will optimize i * 8 as i << 3, that's not guaranteed.

How do we generalize the issue and enhance code readability / maintainability?

XTRAN to the rescue!

Strategy

In order to enhance code readability, we create a preprocessor macro named PWROF2(), and define it in an include file named pwrof2.h:

#define PWROF2(var, pwr) (var << pwr)

We then use XTRAN's pattern match and replace facility to impose the macro on existing code, using 2 match patterns.  In the patterns below, this indicates C / C++ elements and <this> indicates meta (rules language) elements.

(Note — to keep this example simple, we conveniently ignore the possibility that an existing shift is actually there for bit manipulation, not for multiplication.)

The 1st pattern matches each multiplication by a power of 2:

    <var> * <mult>

…where <mult> must be an integer that is a power of 2.  A successful match also computes <pwr>, the power of 2 <mult> is, via a small user meta-function specified in the match pattern.

The 2nd pattern matches each existing shift:

    <var> << <pwr>

The replacement pattern for both match patterns is simply:

    PWROF2(<var>, <pwr>)

…where <var> is whatever matched it in either match pattern, and <pwr> is computed as part of a match of the first pattern, or is whatever matched it in the second pattern.

Both pattern match/replace requests in our rules also tell XTRAN to ensure an #include of pwrof2.h in the re-engineered code.

Example

The XTRAN rules used in this example comprise 29 non-comment lines of meta-code (XTRAN's rules language).  They took ½ hour to write and ½ hour to debug.  (That's right, only 1 hour total!)  They are not very specific to C/C++; they can easily be adapted to work with any language that has a preprocessor.

How can such powerful re-engineering be automated in only one hour using only 29 code lines of XTRAN rules?  Because there is so much capability already available as part of XTRAN's rules language.  The rules used for this example take advantage of the following functionality provided by that rules language:

NOTE that the rendered code shown as XTRAN's output below was done with default conditions; XTRAN provides many options for controlling the way it renders code for output.

The input to and output from XTRAN are untouched.



Process Flowchart

Here is a flowchart for this process, in which the elements are color coded:

data flowchart

Input to XTRAN:

void func(void)
{
        long ivar1, ivar2, ivar3;

        ivar1 = 7 + ivar2 * 8 + 3;              /*matches 1st pattern; <pwr> will be 3*/
        ivar1 = 4 - ivar2 * 7 + 3;              /*doesn't match; 7 isn't 2**n*/
        ivar2 = 6 / (ivar3 << 4) + 3;           /*matches 2nd pattern; <pwr> is 4*/
        return;
}


Output from XTRAN:

#include "pwrof2.h"
void func(void)
{
        long ivar1, ivar2, ivar3;

        ivar1 = 7 + PWROF2(ivar2, 3) + 3;       /*matches 1st pattern; <pwr> will be 3*/
        ivar1 = 4 - ivar2 * 7 + 3;              /*doesn't match; 7 isn't 2**n*/
        ivar2 = 6 / PWROF2(ivar3, 4) + 3;       /*matches 2nd pattern; <pwr> is 4*/
        return;
}