"Stephen Sprunk" <st*****@sprunk.org> writes:
On a project I'm working on, I ran across the following macros:
/* assume s is struct stream *, s->p is char, v is unit16_t or uint32_t */
#define in_uint16_le(s,v) { v = *((s)->p++); v += *((s)->p++) << 8; }
#define in_uint32_le(s,v) { in_uint16_le(s,v) \
v += *((s)->p++) << 16; v += *((s)->p++) << 24; }
First suggestion: these macros can be written in expression form
rather than statement form, and it's probably a good idea to do
that:
#define in_uint16_le(s,v) (v = *((s)->p++), v += *((s)->p++) << 8)
/* etc */
I mention this because this transformation should aid in converting
to a more function-call-like semantics.
I'm personally not fond of function-like macros and wanted to turn these
into static inline functions, but I'm having trouble doing so because the
above macros modify their second argument -- one of the reasons I dislike
them in the first place. This seems to require that the signatures be
changed to:
static inline uint16_t in_uint16_le(struct stream *s);
static inline uint32_t in_uint32_le(struct stream *s);
However, I'm not comfortable both swapping out the macros _and_ rewriting
hundreds of calls to each at the same time, so I'd like some function-like
macros with the new signature as a transition step. I'm also concerned that
there may be compilers out there that puke on "static inline" or don't
optimize it properly, so I'd only use them on platforms where it works
equally well or better.
However, I can't figure out exactly how to write the new macros; can someone
please send reworked versions?
Here is a sketch of an approach. First step:
#define in_uint16_le(s,v) in_puint16_le(s,&(v))
#define in_uint32_le(s,v) in_puint32_le(s,&(v))
#define in_puint16_le(s,v) (*(v) = *((s)->p++), *(v) += *((s)->p++) << 8 )
#define in_puint32_le(s,v) \
(*(v) = *((s)->p++), *(v) += *((s)->p++) << 8, \
*(v) += *((s)->p++) << 16, *(v) += *((s)->p++) << 24)
The first step transitions to macros that use an address, but without
needing to change any of the macro calls (not counting the changes
needed after switching to the expression form, which should be only
adding ;'s in places).
Second step - allow functions in place of macros:
#define in_uint16_le(s,v) in_puint16_le(s,&(v))
#define in_uint32_le(s,v) in_puint32_le(s,&(v))
#if EXPAND_in_puint_X_le_AS_CALLS
# define in_puint16_le(s,v) C_in_puint16_le(s,v)
# define in_puint32_le(s,v) C_in_puint32_le(s,v)
#else
# define in_puint16_le(s,v) CPP_in_puint16_le(s,v)
# define in_puint32_le(s,v) CPP_in_puint32_le(s,v)
#endif
#define CPP_in_puint16_le(s,v) \
(*(v) = *((s)->p++), *(v) += *((s)->p++) << 8 )
#define CPP_in_puint32_le(s,v) \
(*(v) = *((s)->p++), *(v) += *((s)->p++) << 8, \
*(v) += *((s)->p++) << 16, *(v) += *((s)->p++) << 24)
static uint16 inline
C_in_puint16_le( struct stream *s, uint16 *v ){
return CPP_in_puint16_le( s, v );
}
static uint32 inline
C_in_puint32_le( struct stream *s, uint32 *v ){
return CPP_in_puint32_le( s, v );
}
By #define'ing EXPAND_in_puint_X_le_AS_CALLS as 0 or 1,
either CPP expansion behavior or function call behavior
can be generated.
Note 1: to get void results rather than uint16/uint32 results, put
casts in the bottom-most macros, and change the function definitions
appropriately.
Note 2: factoring - it would be nice if the body of the 16 bit macro
didn't have to be replicated. I didn't see any easy way of doing
that, given the type requirements.
Note 3: argument types - if the function call version is used, the
type of the argument v must match the parameter. I count this as a
plus rather than a minus. The CPP expansion version can be used as a
fallback for first compile, comparison debugging, etc.
Note 4: on platforms that don't provide static inline functions, use
CPP directives to exclude the static inline function definitions and
to insure that EXPAND_in_puint_X_le_AS_CALLS will be 0. (That code
was left out above so as not to clutter the example.)
Step 3: if desired, calls such as
in_uint16_le( s, v );
can be changed to
in_puint16_le( s, &v );
where ever they appear. Similarly calls to in_uint32_le.
Disclaimer: I have test compiled code along the lines
of the above, but have not copied/pasted the exact code,
so there may be minor typographical erros.