[comp.lang.c] souped up enum

burow@cernvax.cern.ch (burkhard burow) (11/23/90)

Below, in enum.h, is a souped up 'enum' and an example program follows in
enum.c.

In no more code than a regular typedef enum, the macros ENUMn(...), where n is
the number of constants in the enumeration, provides:
i)   the constants.
ii)  the number of constants.
iii) an array of the constant names as strings.
iv)  a macro to recognize a particular constant in the enumeration.
v)   addition of another constant to the enumeration without changing a single
      line of code elsewhere.

WARNING: The following works on VAX VMS: C 3.1. As far as I know, the only
deviation from ANSI C is the '/**/' kludge for the preprocessor concatenation
operator '##'.

tschuess              INTERNET:  burow%13313.hepnet@csa3.lbl.gov
burkhard                DECNET:  13313::burow

-------------------------cut here---------------------------------------------
/* enum.h */
/* Burkhard Burow, University of Toronto, 1990. */

#ifndef __ENUM_LOADED
#define __ENUM_LOADED	1

#include <stdio.h>
#include <string.h>

/* Souped up enum => ENUM1->ENUMn. Typedef's NAME as enumerated with the n given
   constants. enum_str(NAME) is an array of the constants as strings and NAMES
   is the number of constants. Macro ENUMno recognizes constant from a string.*/

#define enum_str(NAME) ENUM_/**/NAME
#define ENUMno(ENUM_NAME, NAMES, STRING, LENGTH, NO)                           \
 for (NO=0; NO<(int)NAMES; NO++)                                               \
   if (strncmp(STRING,ENUM_NAME[i],LENGTH)==0) break;
/* char *ENUM_NAME[]; int NAMES; char *STRING; int LENGTH; int NO;
  - NO returns enum. tag of ENUM_NAME matching LENGTH characters of STRING.
  - If none of the NAMES tags match STRING, NO returns NAMES.
  - For exact  comparison with STRING      use LENGTH as a_really_big_int.
  - For prefix comparison on   STRING      use LENGTH as strlen(ENUM_NAME[NO]). 
  - For prefix comparison on   ENUM_NAME's use LENGTH as strlen(STRING).      
  - For prefix comparison on   either      use LENGTH as 
                              MIN(strlen(STRING),STRLEN(ENUM_NAME[NO])).      */

#define ENUM1(NAME, A)               typedef enum{A,NAME/**/S}NAME;            \
 static char *enum_str(NAME)[] = {"A"}
#define ENUM2(NAME, A,B)             typedef enum{A,B,NAME/**/S}NAME;          \
 static char *enum_str(NAME)[] = {"A","B"}
#define ENUM3(NAME, A,B,C)           typedef enum{A,B,C,NAME/**/S}NAME;        \
 static char *enum_str(NAME)[] = {"A","B","C"}
#define ENUM4(NAME, A,B,C,D)         typedef enum{A,B,C,D,NAME/**/S}NAME;      \
 static char *enum_str(NAME)[] = {"A","B","C","D"}
#define ENUM5(NAME, A,B,C,D,E)       typedef enum{A,B,C,D,E,NAME/**/S}NAME;    \
 static char *enum_str(NAME)[] = {"A","B","C","D","E"}
#define ENUM6(NAME, A,B,C,D,E,F)     typedef enum{A,B,C,D,E,F,NAME/**/S}NAME;  \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F"}
#define ENUM7(NAME, A,B,C,D,E,F,G)                                             \
 typedef enum{A,B,C,D,E,F,G,NAME/**/S}NAME;                                    \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G"}
#define ENUM8(NAME, A,B,C,D,E,F,G,H)                                           \
 typedef enum{A,B,C,D,E,F,G,H,NAME/**/S}NAME;                                  \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G","H"}
#define ENUM20(NAME, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U)                  \
 typedef enum{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,NAME/**/S}NAME;          \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G","H","I","J","K", \
                                  "L","M","N","O","P","Q","R","T","U"}
#define ENUM46(NAME, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,        \
 AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV)               \
 typedef enum{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,               \
 AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,NAME/**/S}NAME;\
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G","H","I","J","K", \
                      "L","M","N","O","P","Q","R","T","U","V","W","X","Y","Z", \
  "AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK",                      \
  "AL","AM","AN","AO","AP","AQ","AR","AT","AU","AV"}
#define ENUM48(NAME, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,        \
 AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,AW,AX)         \
 typedef enum{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,               \
 AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,AW,AX,         \
 NAME/**/S}NAME;                                                               \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G","H","I","J","K", \
                      "L","M","N","O","P","Q","R","T","U","V","W","X","Y","Z", \
  "AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK",                      \
  "AL","AM","AN","AO","AP","AQ","AR","AT","AU","AV","AW","AX"}
#define EXPAND16(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P)                              \
 A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P
#define QEXPAND16(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P)                             \
 "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P"
/* N.B. e.g. ENUM78(NAME, 52 args, (16 args) ) req.d because VAX C limits 
   number of macro args to 64. */
#define ENUM78(NAME, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,        \
 AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,AW,AX,AY,AZ,   \
 BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,ARGS16)                                   \
 typedef enum{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,               \
  AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,AW,AX,AY,AZ,  \
  BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,EXPAND16/**/ARGS16,NAME/**/S}NAME;       \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G","H","I","J","K", \
                      "L","M","N","O","P","Q","R","T","U","V","W","X","Y","Z", \
  "AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK",                      \
  "AL","AM","AN","AO","AP","AQ","AR","AT","AU","AV","AW","AX","AY","AZ",       \
  "BA","BB","BC","BD","BE","BF","BG","BH","BI","BJ","BK","BL",                 \
  QEXPAND16/**/ARGS16}
#define EXPAND18(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R)                          \
 A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R
#define QEXPAND18(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R)                         \
 "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R"
/* N.B. ENUM80(NAME, 52 args, (18 args) ) syntax req.d because VAX C limits 
   number of macro args to 64. */
#define ENUM80(NAME, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,        \
 AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,AW,AX,AY,AZ,   \
 BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,ARGS18)                                   \
 typedef enum{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z,               \
  AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AT,AU,AV,AW,AX,AY,AZ,  \
  BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,EXPAND18/**/ARGS18,NAME/**/S}NAME;       \
 static char *enum_str(NAME)[] = {"A","B","C","D","E","F","G","H","I","J","K", \
                      "L","M","N","O","P","Q","R","T","U","V","W","X","Y","Z", \
  "AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK",                      \
  "AL","AM","AN","AO","AP","AQ","AR","AT","AU","AV","AW","AX","AY","AZ",       \
  "BA","BB","BC","BD","BE","BF","BG","BH","BI","BJ","BK","BL",                 \
  QEXPAND18/**/ARGS18}


#endif					/* __ENUM_LOADED */
-------------------------cut here---------------------------------------------
/* enum.c  An example of the macros in enum.h */
/* Burkhard Burow, University of Toronto, 1990. */

#include "enum.h"
#include <stdio.h>

#define MIN(A,B) ((A)<(B)?(A):(B))

/* Note that adding another constant to the following line requires no change
in any other line of code. */
ENUM4(LIST, JUST, SOME, SILLY, STUFF);

main ()
{
int i, loop; char s[99]; 
LIST list;

puts("The enum 'list' has the following enumerated constants.");
for (i=0; i<(int)LISTS; i++) printf("  %s", enum_str(LIST)[i]);

puts("\n Please enter one of the above constants: ");
gets(s);

for (loop=0; loop<4; loop++) {
  switch (loop) {
  case 0: printf("An exact comparison\n");
          ENUMno(enum_str(LIST), LISTS, s, 9999,                      i);
          break;
  case 1: printf("A comparison, using the prefix of %s, \n", s);
          ENUMno(enum_str(LIST), LISTS, s, strlen(enum_str(LIST)[i]), i);
          break;
  case 2: printf("A comparison, using the prefices of 'list's' constants,\n");
          ENUMno(enum_str(LIST), LISTS, s, strlen(s),                 i);
          break;
  case 3: printf("A comparison, using the prefices of either,\n");
          ENUMno(enum_str(LIST), LISTS, s, 
                            MIN(strlen(s),strlen(enum_str(LIST)[i])), i);
          break;
  }
  if (i==(int)LISTS) 
    printf(" does not recognize %s as a constant in 'list'\n\n", s);
  else 
    printf(" recognizes %s as the constant %s in 'list'\n\n", s,
           enum_str(LIST)[i]);
}
}