From owner-freebsd-hackers@FreeBSD.ORG Tue Dec 13 18:35:19 2005 Return-Path: X-Original-To: freebsd-hackers@freebsd.org Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 6CDDC16A41F for ; Tue, 13 Dec 2005 18:35:19 +0000 (GMT) (envelope-from hselasky@c2i.net) Received: from swip.net (mailfe01.swip.net [212.247.154.1]) by mx1.FreeBSD.org (Postfix) with ESMTP id 082D843D64 for ; Tue, 13 Dec 2005 18:35:14 +0000 (GMT) (envelope-from hselasky@c2i.net) X-T2-Posting-ID: Y1QAsIk9O44SO+J/q9KNyQ== X-Cloudmark-Score: 0.000000 [] Received: from mp-217-37-239.daxnet.no ([193.217.37.239] verified) by mailfe01.swip.net (CommuniGate Pro SMTP 5.0.2) with ESMTP id 46128512 for freebsd-hackers@freebsd.org; Tue, 13 Dec 2005 19:34:56 +0100 From: Hans Petter Selasky To: freebsd-hackers@freebsd.org Date: Tue, 13 Dec 2005 19:36:10 +0100 User-Agent: KMail/1.7 MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200512131936.11640.hselasky@c2i.net> Subject: Standard C-macro scripting X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: hselasky@c2i.net List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 13 Dec 2005 18:35:19 -0000 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