Generic Programming In ANY Language Using The m4 Macro Processor

by Dipl. Ing. Frank Gerlach(frankgerlach.tai@gmx.de)

The following m4 macro code demonstrates typesafe genering programming in C. Unlike the C qsort() procedure, the following code is type safe. Unlike C++ templates/STL, the generated code yields nice error messages.

Advantages of Approach

  • Generated Code can be easily inspected by editor
  • Generated Code can be easily indexed by IDE, ctags etc.
  • Generated Code can be easily handled by debugger
  • Compiler can generate easy-to-understand error messages, pointing into generated code
  • In the spirit of Unix, compilers are not bloated
  • KISS Approach
  • Real World Use

    I have seen this approach being used by a major, large-scale CAD program with thousands of modules and developers. This project uses the C preprocessor to generate generic C++ data structures such as container classes. The program is very successful in many industries such as automotive and aerospace.

    Example - Strongly Typed Quicksort - quicksort.m4

    
    define(swap_generic,
       {
       $1 temp;
       temp = *$2;
       *$2 = *$3;
       *$3 = temp;
       }
    )
    
    define(swap_generic_ptr,
       {
       $1* temp;
       temp = $2[$3];
       $2[$3] = $2[$4];
       $2[$4] = temp;
       }
    )
    
    define(quicksort_typesafe, 
    
    extern int less_than_$1($1,$1);
    
    void quicksort_$1($1 *begin, $1 *end) 
    { 
      $1 *ptr; 
      $1 *split; 
      if (end - begin < 1) 
      {   
          return; 
      } 
      ptr = begin; 
      split = begin + 1; 
      while (++ptr <= end) 
      { 
          if (less_than_$1(*ptr,*begin)) 
          { 
              swap_generic($1,ptr, split) 
              ++split; 
          } 
      } 
      $1* spm1 = split -1;
      swap_generic($1,begin, spm1)
      quicksort_$1(begin, spm1); 
      quicksort_$1(split, end); 
    })
    
    define(quicksort_typesafe_ptr, 
    
    extern int less_than_ptr_$1($1**,int,int);
    
    
    void quicksort_ptr_$1($1** array, int begin, int end) 
    { 
      int ptr; 
      int split; 
      if (end - begin < 1) 
      {   
          return; 
      } 
      ptr = begin; 
      split = begin + 1; 
      while (++ptr <= end) 
      { 
          if (less_than_ptr_$1(array,ptr,begin)) 
          { 
              swap_generic_ptr($1,array,ptr, split) 
              ++split; 
          } 
      } 
      int spm1 = split -1;
      swap_generic_ptr($1,array,begin, spm1)
      quicksort_ptr_$1(array,begin, spm1); 
      quicksort_ptr_$1(array,split, end); 
    })
    
    quicksort_typesafe(int);
    
    quicksort_typesafe_ptr(strCust);
    
    

    main.c

    
    #include 
    #include 
    #include 
    
    struct Customer
    {
       char* name;
       char* street;
       char* city;
       int postcode;
    };
    
    
    typedef struct Customer strCust;
    
    int less_than_ptr_strCust(strCust** array,int posA,int posB)
    {
        return strcmp(array[posA]->name, array[posB]->name) < 0; 
    }
    
    #include "qs_int.c"
    
    void quicksort_int(int*, int*);
    
    int less_than_int(int a, int b)
    {
      return a < b;
    }
    
    int main(int argc, char** argv)
    {
        const int n=20;
        int toBeSorted[20] = {999,4,55,2,1,1,8888,8,6,4,3,2,1,8888,5,5,5,5,5,5};
    
        quicksort_int(toBeSorted,toBeSorted+n-1);
    
        for(int i=0; i < n; i++)
        {
           printf("%i ",toBeSorted[i]);
        }
        printf("\n");
    
        strCust* customerList[6];
    
        customerList[0] = malloc(sizeof(strCust));
        customerList[0]->name = strdup("Boeing Co");
        customerList[0]->street = strdup("Grant Avenue");
        customerList[0]->city = strdup("Everett");
        customerList[0]->postcode = 11111;
    
        customerList[1] = malloc(sizeof(strCust));
        customerList[1]->name = strdup("Airbus SE");
        customerList[1]->street = strdup("Rue de General Foche");
        customerList[1]->city = strdup("Toulouse");
        customerList[1]->postcode = 11112;
    
        customerList[2] = malloc(sizeof(strCust));
        customerList[2]->name = strdup("Mercedes Benz");
        customerList[2]->street = strdup("Daimlerstr");
        customerList[2]->city = strdup("Stuttgart");
        customerList[2]->postcode = 21111;
    
        customerList[3] = malloc(sizeof(strCust));
        customerList[3]->name = strdup("Dassault Aviation");
        customerList[3]->street = strdup("Avenue Charles de Gaulle");
        customerList[3]->city = strdup("Paris");
        customerList[3]->postcode = 14111;
    
        customerList[4] = malloc(sizeof(strCust));
        customerList[4]->name = strdup("BAE Systems");
        customerList[4]->street = strdup("Nelson Square");
        customerList[4]->city = strdup("London");
        customerList[4]->postcode = 71111;
    
        customerList[5] = malloc(sizeof(strCust));
        customerList[5]->name = strdup("Saab");
        customerList[5]->street = strdup("Akerbogatan");
        customerList[5]->city = strdup("Linkoeping");
        customerList[5]->postcode = 17111;
    
        quicksort_ptr_strCust(customerList,0,5);
    
        for(int i=0; i < 6; i++)
        {
           printf("%s\n",customerList[i]->name);
        }
    }
    
    
    

    References, Further Reading

    Exploiting the m4 Macro Language

    Author