Standard C-macro scripting
Hans Petter Selasky
hselasky at c2i.net
Tue Dec 13 10:35:19 PST 2005
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
More information about the freebsd-hackers
mailing list