Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 13 Dec 2005 19:36:10 +0100
From:      Hans Petter Selasky <hselasky@c2i.net>
To:        freebsd-hackers@freebsd.org
Subject:   Standard C-macro scripting
Message-ID:  <200512131936.11640.hselasky@c2i.net>

next in thread | raw e-mail | index | archive | help
Hi,

What do you think about defining the following macros like this:

#ifndef NOT
#define NOT(arg) _NOT(YES arg(() NO))
#define          _NOT(args...) args
#endif

#ifndef YES
#define YES(args...) args
#define NO(args...)
#endif

#ifndef END
#define END(args...) args
#endif

#if ((1-YES(1)) || (1-NOT(NO)(1)) NO(||1))
#error "macros are not expanded correctly"
#endif

After some thinking these macros prove very useful building blocks. Consider 
the following macro:

#define IE_WORD_COMPILE_1(name, def, decode)    \
  decode(u_int8_t name##_WORD;)                 \
  u_int16_t name;

Depending on wether the "decode" argument is "YES" or "NO", code will be kept 
or removed respectivly. Also the "decode" argument can be negated: 
"NOT(decode)". Usually "YES" and "NO" are not used directly but indirectly. 
For example one has a definition of a structure presented like this:

#define CAPI_FACILITY_CONF(m,n) \
  m(n, WORD  , wInfo,)\
  m(n, WORD  , wSelector,)\
  m(n, STRUCT, Param,)\
  END

Using this definition one wants to generate several other structures and 
initializers, that initialize the fields depending on their type, all 
automatic. How can one do that by using C-macros?


Here is the code to generate "struct CAPI_FACILITY_CONF_ENCODED":

CAPI_MAKE_STRUCT(CAPI_FACILITY_CONF);


Here is the kernel code that uses this structure and initializes it:

/*---------------------------------------------------------------------------*
 *      generate facility confirmation message
 *---------------------------------------------------------------------------*/
static struct mbuf *
capi_make_facility_conf(struct capi_message_encoded *pmsg, 
                        u_int16_t wSelector, 
                        u_int16_t wInfo)
{
        struct mbuf *m;
        struct capi_message_encoded msg;
        struct CAPI_FACILITY_CONF_DECODED fac_conf = { /* zero */ };

        u_int16_t len;

        /* the "CAPI_INIT" macro is defined further down */
        CAPI_INIT(CAPI_FACILITY_CONF, &fac_conf); 

        fac_conf.wInfo = wInfo;
        fac_conf.wSelector = wSelector;

        len = capi_encode(&msg.data, sizeof(msg.data), &fac_conf);
        len += sizeof(msg.head);

        /* fill out CAPI header */

        msg.head.wLen = htole16(len);
        msg.head.wApp = htole16(0);
        msg.head.wCmd = htole16(CAPI_CONF(FACILITY));
        msg.head.wNum = htole16(pmsg->head.wNum);
        msg.head.dwCid = htole32(pmsg->head.dwCid);

        if((m = i4b_getmbuf(len, M_NOWAIT)))
        {
            bcopy(&msg, m->m_data, m->m_len);
        }
        return m;
}

Here are the macros that do all the generation, and some extra ones that 
generate other things:

/* information element types 
 * for internal use:
 */
#define IE_END                  0
#define IE_BYTE                 1
#define IE_WORD                 2
#define IE_DWORD                3
#define IE_QWORD                4
#define IE_STRUCT               5 /* excludes length field */
#define IE_STRUCT_CAPI          6 /* includes length field */
#define IE_STRUCT_DECODED       7
#define IE_STRUCT_DECODED_EMPTY 8
#define IE_BYTE_ARRAY           9
#define IE_MAX                  10 /* exclusive */

struct capi_struct {
  u_int16_t len;
  void * ptr;
} __packed;

#define IE_BYTE_COMPILE_M(b,w,d,q,s,a) b
#define IE_BYTE_COMPILE_1(name, def, decode)    \
  decode(u_int8_t name##_BYTE;)                 \
  u_int8_t name;

#define IE_WORD_COMPILE_M(b,w,d,q,s,a) w
#define IE_WORD_COMPILE_1(name, def, decode)    \
  decode(u_int8_t name##_WORD;)                 \
  u_int16_t name;

#define IE_DWORD_COMPILE_M(b,w,d,q,s,a) d
#define IE_DWORD_COMPILE_1(name, def, decode)   \
  decode(u_int8_t name##_DWORD;)                \
  u_int32_t name;

#define IE_QWORD_COMPILE_M(b,w,d,q,s,a) q
#define IE_QWORD_COMPILE_1(name, def, decode)   \
  decode(u_int8_t name##_QWORD;)                \
  u_int64_t name;

#define IE_STRUCT_COMPILE_M(b,w,d,q,s,a) s
#define IE_STRUCT_COMPILE_1(name, def, decode)  \
  decode(u_int8_t name##_STRUCT;                \
         struct capi_struct name;)              \
  NOT(decode)(/* structure length is set to     \
               * zero. Use the *DECODED         \
               * structure if length is non-    \
               * zero.                          \
               */                               \
              u_int8_t name##_Null;)

#define IE_BYTE_ARRAY_COMPILE_M(b,w,d,q,s,a) a
#define IE_BYTE_ARRAY_COMPILE_1(name, def, decode)      \
  decode(u_int8_t name##_BYTE_ARRAY;                    \
         u_int16_t name##_BYTE_ARRAY_LENGTH; )          \
   u_int8_t name [def];

#define CAPI_MAKE_DECODED_FIELD(n, type, field, def) \
  IE_##type##_COMPILE_1(field, def, YES)

#define CAPI_MAKE_ENCODED_FIELD(n, type, field, def) \
  IE_##type##_COMPILE_1(field, def, NO)

#define CAPI_MAKE_STRUCT(name)                  \
  struct name##_DECODED {                       \
    name(CAPI_MAKE_DECODED_FIELD,)()            \
    u_int8_t name##_end;                        \
  } __packed;                                   \
  struct name##_ENCODED {                       \
    name(CAPI_MAKE_ENCODED_FIELD,)()            \
  } __packed;

#define CAPI_MAKE_DEF_1(n,ENUM,value)           \
  enum { CAPI_##ENUM = (value) };               \
  CAPI_MAKE_STRUCT(CAPI_##ENUM##_REQ)           \
  CAPI_MAKE_STRUCT(CAPI_##ENUM##_CONF)          \
  CAPI_MAKE_STRUCT(CAPI_##ENUM##_IND)           \
  CAPI_MAKE_STRUCT(CAPI_##ENUM##_RESP)

#define CAPI_MAKE_DEF_2(n,ENUM,value)           \
  CAPI_##ENUM##_INDEX,

#define CAPI_MAKE_DEF_3(n, ENUM) \
  CAPI_MAKE_STRUCT(CAPI_##ENUM)

#define CAPI_MAKE_UNION_1(n,ENUM,value)         \
  struct CAPI_##ENUM##_REQ_##n ENUM##_REQ;      \
  struct CAPI_##ENUM##_CONF_##n ENUM##_CONF;    \
  struct CAPI_##ENUM##_IND_##n ENUM##_IND;      \
  struct CAPI_##ENUM##_RESP_##n ENUM##_RESP;

#define CAPI_MAKE_UNION_2(n, ENUM) \
  struct CAPI_##ENUM##_##n ENUM;

#define CAPI_DEBUG_OFFSET_1(field) \
  (((u_int8_t *)&(((struct capi_message_decoded *)0)->field)) - ((u_int8_t *)
0))

/* this macro is a copy of the
 * next macro and is used to
 * allow macro recursion
 */
#define CAPI_MAKE_DEBUG_3(n, type, field, def)                          \
  IE_##type##_COMPILE_M                                                 \
  (                                                                     \
     YES({ IE_BYTE, 1, CAPI_DEBUG_OFFSET_1(n.field), #field },)         \
   ,                                                                    \
     YES({ IE_WORD, 2, CAPI_DEBUG_OFFSET_1(n.field), #field },)         \
   ,                                                                    \
     YES({ IE_DWORD, 4, CAPI_DEBUG_OFFSET_1(n.field), #field },)        \
   ,                                                                    \
     YES({ IE_QWORD, 8, CAPI_DEBUG_OFFSET_1(n.field), #field },)        \
   ,                                                                    \
     CAPI_##def(NO,)                                                    \
     (                                                                  \
        CAPI_##def(CAPI_MAKE_DEBUG_3, def)()                            \
     )                                                                  \
     NOT(CAPI_##def(NO,))                                               \
     (                                                                  \
       { IE_STRUCT, sizeof(void *),                                     \
           CAPI_DEBUG_OFFSET_1(n.field.ptr), #field ".ptr" },           \
     )                                                                  \
   ,                                                                    \
      YES({ IE_BYTE_ARRAY, sizeof(u_int8_t [def]),                      \
              CAPI_DEBUG_OFFSET_1(n.field), #field "[" #def "]"},)      \
  )

/* this macro is used to make the debug
 * table in "libcapi.c"
 */
#define CAPI_MAKE_DEBUG_2(n, type, field, def)                          \
  IE_##type##_COMPILE_M                                                 \
  (                                                                     \
     YES({ IE_BYTE, 1, CAPI_DEBUG_OFFSET_1(n.field), #field },)         \
   ,                                                                    \
     YES({ IE_WORD, 2, CAPI_DEBUG_OFFSET_1(n.field), #field },)         \
   ,                                                                    \
     YES({ IE_DWORD, 4, CAPI_DEBUG_OFFSET_1(n.field), #field },)        \
   ,                                                                    \
     YES({ IE_QWORD, 8, CAPI_DEBUG_OFFSET_1(n.field), #field },)        \
   ,                                                                    \
     CAPI_##def(NO,)                                                    \
     (                                                                  \
        CAPI_##def(CAPI_MAKE_DEBUG_3, def)()                            \
     )                                                                  \
     NOT(CAPI_##def(NO,))                                               \
     (                                                                  \
       { IE_STRUCT, sizeof(void *),                                     \
           CAPI_DEBUG_OFFSET_1(n.field.ptr), #field ".ptr" },           \
     )                                                                  \
   ,                                                                    \
      YES({ IE_BYTE_ARRAY, sizeof(u_int8_t [def]),                      \
              CAPI_DEBUG_OFFSET_1(n.field), #field "[" #def "]"},)      \
  )

#define CAPI_MAKE_DEBUG_1(n, ENUM, value)                               \
  { IE_END, CAPI_P_REQ(ENUM), CAPI_REQ(ENUM), "CAPI_" #ENUM "_REQ" },   \
  CAPI_##ENUM##_REQ (CAPI_MAKE_DEBUG_2, data.ENUM##_REQ )()             \
  { IE_END, CAPI_P_CONF(ENUM), CAPI_CONF(ENUM), "CAPI_" #ENUM "_CONF" }, \
  CAPI_##ENUM##_CONF(CAPI_MAKE_DEBUG_2, data.ENUM##_CONF)()             \
  { IE_END, CAPI_P_IND(ENUM), CAPI_IND(ENUM), "CAPI_" #ENUM "_IND" },   \
  CAPI_##ENUM##_IND (CAPI_MAKE_DEBUG_2, data.ENUM##_IND )()             \
  { IE_END, CAPI_P_RESP(ENUM), CAPI_RESP(ENUM), "CAPI_" #ENUM "_RESP" }, \
  CAPI_##ENUM##_RESP(CAPI_MAKE_DEBUG_2, data.ENUM##_RESP)()

#define CAPI_MAKE_CASES(n, ENUM, value)                         \
  case CAPI_P_REQ(ENUM):                                        \
    CAPI_INIT_2(CAPI_##ENUM##_REQ,&((n)->data.ENUM##_REQ))      \
    break;                                                      \
  case CAPI_P_CONF(ENUM):                                       \
    CAPI_INIT_2(CAPI_##ENUM##_CONF,&((n)->data.ENUM##_CONF))    \
    break;                                                      \
  case CAPI_P_IND(ENUM):                                        \
    CAPI_INIT_2(CAPI_##ENUM##_IND,&((n)->data.ENUM##_IND))      \
    break;                                                      \
  case CAPI_P_RESP(ENUM):                                       \
    CAPI_INIT_2(CAPI_##ENUM##_RESP,&((n)->data.ENUM##_RESP))    \
    break;

#define CAPI_(m,n) NO /* invalid structure */

#define CAPI_MAKE_INIT_1(n, type, field, def)                   \
  (n)->field##_##type = IE_##type;                              \
  IE_##type##_COMPILE_M                                         \
  (,,,,,(n)->field##_##type##_LENGTH = sizeof(u_int8_t [def]);)

#if (IE_STRUCT_DECODED_EMPTY == 0)
#error "IE_STRUCT_DECODED_EMPTY cannot be zero"
#endif

#define CAPI_MAKE_INIT_2(n, type, field, def)                   \
  IE_##type##_COMPILE_M                                         \
  (                                                             \
     /* BYTE */                                                 \
     (n)->field##_##type = IE_##type;                           \
   ,                                                            \
     /* WORD */                                                 \
     (n)->field##_##type = IE_##type;                           \
   ,                                                            \
     /* DWORD */                                                \
     (n)->field##_##type = IE_##type;                           \
   ,                                                            \
     /* QWORD */                                                \
     (n)->field##_##type = IE_##type;                           \
   ,                                                            \
     /* STRUCT */                                               \
     CAPI_##def(NO,)                                            \
     (                                                          \
      /* allow the application to set type */                   \
      if((n)->field##_##type != IE_STRUCT_DECODED_EMPTY)        \
        { (n)->field##_##type = IE_STRUCT_DECODED; }            \
                                                                \
        /* use hints set by the environment to                  \
         * setup the *DECODED pointer and structure             \
         */                                                     \
        (n)->field.ptr = &def##_STRUCT;                         \
        def##_INIT = 1;                                         \
     )                                                          \
     NOT(CAPI_##def(NO,))                                       \
     (                                                          \
      /* standard CAPI structure pointer */                     \
      (n)->field##_##type = IE_##type##_CAPI;                   \
     )                                                          \
   ,                                                            \
     /* BYTE ARRAY */                                           \
     (n)->field##_##type = IE_##type;                           \
     (n)->field##_##type##_LENGTH = sizeof(u_int8_t [def]);     \
   )

/* this macro is used to initialize
 * the *DECODED structures before
 * passed to "capi_encode()" or
 * "capi_decode()"
 */
#define CAPI_INIT(what, ptr)                    \
  { what(CAPI_MAKE_INIT_1,ptr)();               \
    (ptr)->what##_end = IE_END; }               \
/**/

/* internal use macro */
#define CAPI_INIT_2(what, ptr)                  \
  { what(CAPI_MAKE_INIT_2,ptr)();               \
    (ptr)->what##_end = IE_END; }               \
/**/



Of course I chain things. When I whant to generate all structures in my header 
file I do it by typing:

#define CAPI_COMMANDS(m,n) \
/*m(n, enum                         , value )*  \
 *m(n,------------------------------,-------)*/ \
  m(n, DATA_B3                      , 0x0086)   \
  m(n, CONNECT                      , 0x0002)   \
  m(n, CONNECT_ACTIVE               , 0x0003)   \
  m(n, CONNECT_B3                   , 0x0082)   \
  m(n, CONNECT_B3_ACTIVE            , 0x0083)   \
  m(n, CONNECT_B3_T90_ACTIVE        , 0x0088)   \
  m(n, DISCONNECT                   , 0x0004)   \
  m(n, DISCONNECT_B3                , 0x0084)   \
  m(n, ALERT                        , 0x0001)   \
  m(n, INFO                         , 0x0008)   \
  m(n, SELECT_B_PROTOCOL            , 0x0041)   \
  m(n, FACILITY                     , 0x0080)   \
  m(n, RESET_B3                     , 0x0087)   \
  m(n, MANUFACTURER                 , 0x00FF)   \
  m(n, LISTEN                       , 0x0005)   \
/**/

/* for each command generate eight structures */
CAPI_COMMANDS(CAPI_MAKE_DEF_1,);



What do you think about using C-macros like a scripting language?



Any comments?



--HPS



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200512131936.11640.hselasky>