c macros & terse c

January 18, 2015

C macro magic is typically frowned upon in the developer community. Heavy preprocessor usage can make code hard to read, expansion difficult to understand, and the resulting code error prone. While all these things are true, macros do have nice uses cases such as in the implementation of high performance generic containers in C (see klib).

I have also encountered significant code bases in which macros where used as a semantic overlay easing the programmer’s transition from their preferred language into C. One such example is Kx System’s kdb+, Arthur Whitney’s high performance database system which is widely deployed in the financial industry.

Arthur Whitney designed the K and Q programming languages and his love for terse code has translated into their implementations. Following is a short code block from one of kdb+’s include files k.h:

#ifndef KX
#define KX
typedef char*S,C;typedef unsigned char G;typedef short H;typedef int I;typedef long long J;typedef float E;typedef double F;typedef void V;
#ifdef __cplusplus
extern"C"{
#endif
#ifndef KXVER
#error "Set KXVER=3 for kdb+3.0 or standalone c-api after 2011-04-20. Otherwise set KXVER=2"
#endif
#if KXVER>=3
typedef struct k0{signed char m,a,t;C u;I r;union{G g;H h;I i;J j;E e;F f;S s;struct k0*k;struct{J n;G G0[1];};};}*K;
typedef struct{G g[16];}U;
#define kU(x) ((U*)kG(x))
#define xU ((U*)xG)
extern K ku(U),ktn(I,J),kpn(S,J);
extern I setm(I);
#define DO(n,x)	{J i=0,_i=(n);for(;i<_i;++i){x;}}
#else
typedef struct k0{I r;H t,u;union{G g;H h;I i;J j;E e;F f;S s;struct k0*k;struct{I n;G G0[1];};};}*K;
extern K ktn(I,I),kpn(S,I);
#define DO(n,x)	{I i=0,_i=(n);for(;i<_i;++i){x;}}
#endif
#ifdef __cplusplus
}
#endif
//#include<string.h>
// vector accessors, e.g. kF(x)[i] for float&datetime
#define kG(x)	((x)->G0)
#define kC(x)	kG(x)
#define kH(x)	((H*)kG(x))
#define kI(x)	((I*)kG(x))
#define kJ(x)	((J*)kG(x))
#define kE(x)	((E*)kG(x))
#define kF(x)	((F*)kG(x))
#define kS(x)	((S*)kG(x))
#define kK(x)	((K*)kG(x))
.....

A cursory glance reveals that types are aliased with very short identifiers. Accessor macros are provided for different struct values and there is also a DO loop.

The source file c.c shows how these macros are used and hints at what the entire kdb+ code base probably looks like.

#define Q(e,s) {if(e)return printf("%s\n",s),-1;}      //error

int main(){K x,y;int c=khpu("localhost",5001,"usr:pwd");
 Q(c<0,"connect")Q(!c,"access")
 Q(!k(-c,"`trade insert(12:34:56.789;`xx;93.5;300)",(K)0),"err") // statement insert
 Q(!(x=k(c,"select sum size by sym from trade",0)),"err")    // statement select
 Q(!(x=ktd(x)),"type")                   // flip from keyedtable(dict)
  y=x->k;                                // dict from flip
  y=kK(y)[1],printf("%d cols:\n",y->n);  // data from dict
  y=kK(y)[0],printf("%d rows:\n",y->n);  // column 0
  printf("%s\n",kS(y)[0]);               // sym 0 
  r0(x);                                 // release memory
 x=knk(4,kt(1000*(time(0)%86400)),ks("xx"),kf(93.5),ki(300)); // data record
// DO(10000,Q(!k(-c,"insert",ks((S)"trade"),r1(x),(K)0),es)) // 10000 asynch inserts
// k(c,"",(K)0); // synch chase
// return 0;
 Q(!k(-c,"insert",ks("trade"),x,(K)0),"err")                           // parameter insert
 Q(!(x=k(c,"{[x]select from trade where size>x}",ki(100),(K)0)),"err") // parameter select
  r0(x);
 close(c);
 return 0;}
*/

The code is terse, but for someone with Arthur’s background, easy to read and write. Essentially he is mapping his stylistic preference for APL based languages onto C. While there is a certain elegeance to his approach, I do not recommend copying his style and becoming a macro addict in your large production applications which need to survive maintenance cycles.

If you’re interested in exploring the world of array programming languages, J is a powerful open source implementation.

comments powered by Disqus