Standard C-macro scripting

Hans Petter Selasky hselasky at c2i.net
Tue Dec 13 16:04:39 PST 2005


On Tuesday 13 December 2005 20:48, John Baldwin wrote:
> On Tuesday 13 December 2005 01:36 pm, Hans Petter Selasky wrote:
> > Hi,
> >
> > ...
> >
> > What do you think about using C-macros like a scripting language?
>
> Honestly, I think I've now been scarred for life. :-/  I think that this
> stuff would be so obscure that no one else would be able to help with
> maintenace.

Macros are easy. It is just concat, stringify and expand. Maybe you have to 
think more about it before you get it. I am probably too used to it.

What is the alternative? An Awk script would require a lot more code and it 
cannot be called from a C-program when it is compiling.

Consider the following: One has some "enums" and wants to declare these and 
make a debugging table at the same time. The problem is that at some time one 
adds another "enum" and then the debugging table is out of sync. Using an 
Awk/Perl/XXX script to keep things in sync would be overkill.

If one makes some standard macros that do standard transforms, then people 
will not get confused. I have started out to standardise this, for example 
one macro to make enums, MAKE_ENUM, and another to make tables of different 
kinds, MAKE_TABLE. There might be room for improvement.

#define _MAKE_ENUM(enum,value,arg...)           \
        enum value,                             \
/**/
#define MAKE_ENUM(macro,end...) \
enum { macro(_MAKE_ENUM) end }  \
/**/

#define __MAKE_TABLE(a...) a    /* double pass to expand all macros */
#define _MAKE_TABLE(a...) (a),  /* add comma */
#define MAKE_TABLE(m,field,p,a...) m##_##field p = \
    { __MAKE_TABLE(m(m##_##field _MAKE_TABLE)) a }

What the macros above do, is not difficult to understand at all. It is bare 
simple.


Here is the definition of some enum series:

#define MY_ENUMS_DEFAULT_DRIVER_DESC(enum,val,desc) desc
#define MY_ENUMS(m)\
m(MY_ENUM_UNKNOWN  ,,"unknown")\
m(MY_ENUM_YYY      ,,"yyy ...")\
m(MY_ENUM_DAIC     ,,"zzz ...")\
/**/

Here is the declaration:

MAKE_ENUM(MY_ENUMS,
        N_MY_ENUMS);

Here is the debugging table:

static const char * const 
  MAKE_TABLE(MY_ENUMS,DEFAULT_DRIVER_DESC,[]);

Because the "MAKE_ENUM" macro only use the two first arguments passed to "m" 
in "MY_ENUMS", the other arguments can be used for other purpose, and one can 
list more information:


Here is a real example of a state machine:



#define L3_STATES(m)/* \
m(----------------,,--------,-------------,-------------------------,------)*\
m(                ,,timeout , timeout     ,                         , q931 )*\
m( state          ,,delay   , state       , desc                    , conv.)*\
m(----------------,,--------,-------------,-------------------------,------)*/ 
\
m( ST_L3_U0       ,, 0/*hz*/, ST_L3_U0    , "Null"                  , 0x00  )\
                                                                             \
m( ST_L3_OUTGOING ,, 8/*hz*/, ST_L3_U0    , "Outgoing initialized"  , 0x00  )\
m( ST_L3_U1       ,, 8/*hz*/, ST_L3_U0    , "Outgoing setup (U1)"   , 0x01  )\
m( ST_L3_U2       ,,16/*hz*/, ST_L3_U0    , "Outgoing setup (U2)"   , 0x02  )\
m( ST_L3_U2_ACK   ,,16/*hz*/, ST_L3_U0    , "Outgoing setup (U2)"   , 0x02  )\
m( ST_L3_U3       ,, 8/*hz*/, ST_L3_U3_TO , "Outgoing proceeding"   , 0x03  )\
m( ST_L3_U3_TO    ,, 4/*hz*/, ST_L3_U0    , "Outgoing proceeding"   , 0x03  )\
                                                                             \
m( ST_L3_U4       ,, 8/*hz*/, ST_L3_U4_TO , "Outgoing delivered"    , 0x04  )\
m( ST_L3_U4_TO    ,, 4/*hz*/, ST_L3_U0    , "Outgoing delivered"    , 0x04  )\
                                                                             \
m( ST_L3_INCOMING ,, 8/*hz*/, ST_L3_U0    , "Incoming initialized"  , 0x00  )\
m( ST_L3_IN_ACK   ,,16/*hz*/, ST_L3_U0    , "Incoming initialized"  , 0x19  )\
m( ST_L3_U6       ,, 8/*hz*/, ST_L3_U0    , "Incoming present"      , 0x06  )\
m( ST_L3_U7       ,, 8/*hz*/, ST_L3_U7_TO , "Incoming alerted"      , 0x07  )\
m( ST_L3_U7_TO    ,, 4/*hz*/, ST_L3_U0    , "Incoming alerted"      , 0x07  )\
m( ST_L3_U8       ,, 4/*hz*/, ST_L3_U0    , "Incoming connecting"   , 0x08  )\
                                                                             \
m( ST_L3_UA       ,, 8/*hz*/, ST_L3_UA_TO , "Active"                , 0x0A  )\
m( ST_L3_UA_TO    ,, 4/*hz*/, ST_L3_U0    , "Active"                , 0x0A  )\
                                                                             \
m( ST_L3_UC       ,, 8/*hz*/, ST_L3_UC_TO , "Disconnected"          , 0x0C  )\
m( ST_L3_UC_TO    ,, 4/*hz*/, ST_L3_U0    , "Disconnected"          , 0x0C  )\
/**/

Isn't the state-machine above easy to edit and understand ?

What is wrong about that?

And how would you solve it?

--HPS


More information about the freebsd-hackers mailing list