Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Help with function-like macros

Reply
Thread Tools

Help with function-like macros

 
 
Stephen Sprunk
Guest
Posts: n/a
 
      12-31-2004
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; }

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?

S

--
Stephen Sprunk "Stupid people surround themselves with smart
CCIE #3723 people. Smart people surround themselves with
K5SSS smart people who disagree with them." --Aaron Sorkin

 
Reply With Quote
 
 
 
 
Michael Mair
Guest
Posts: n/a
 
      12-31-2004


Stephen Sprunk wrote:
> 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; }


I would rather use the do....while (0) form (two threads above this
one).
s->p seems to be of type char * instead of char, as indicated above.
If this is true: Consider the use of unsigned char if you can.

> 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);


Why not stay with the "interface" given by the macro?

static inline void in_uint16_le(struct stream *s, uint16_t *v);
static inline void in_uint32_le(struct stream *s, uint32_t *v);

Replacing in_uintN_t(s,v) by in_uintN_t(s,&(v)) is easier.

Notes: You did not comment on the change of the value of p,
so I guess that this is intended.
Note also that uint16_t is not guaranteed by the C99 standard
whereas uint_least16_t and uint_fast16_t are (same for 8, 32, 64 and
other numbers of bits).


> 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?


The ugly thing is that you have to work with the comma operator.
Consider

#include <stdio.h>

#define get16(p) ((p)+=2, *((p)-2)+(*((p)-1)<<)

int main (void)
{
unsigned char *ptr, arr[]={255,255};

ptr = arr;

printf("%u\n",get16(ptr));

return 0;
}


Cheers
Michael
--
E-Mail: Mine is a gmx dot de address.

 
Reply With Quote
 
 
 
 
Michael Mair
Guest
Posts: n/a
 
      12-31-2004


Michael Mair wrote:
>
>
> Stephen Sprunk wrote:
>
>> 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; }

>
>
> I would rather use the do....while (0) form (two threads above this
> one).
> s->p seems to be of type char * instead of char, as indicated above.
> If this is true: Consider the use of unsigned char if you can.
>
>> 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);

>
>
> Why not stay with the "interface" given by the macro?
>
> static inline void in_uint16_le(struct stream *s, uint16_t *v);
> static inline void in_uint32_le(struct stream *s, uint32_t *v);
>
> Replacing in_uintN_t(s,v) by in_uintN_t(s,&(v)) is easier.
>
> Notes: You did not comment on the change of the value of p,
> so I guess that this is intended.
> Note also that uint16_t is not guaranteed by the C99 standard
> whereas uint_least16_t and uint_fast16_t are (same for 8, 32, 64 and
> other numbers of bits).
>
>
>> 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?

>
>
> The ugly thing is that you have to work with the comma operator.
> Consider
>
> #include <stdio.h>
>
> #define get16(p) ((p)+=2, *((p)-2)+(*((p)-1)<<)


Just thought about it; for the record:
"& 0xFF" makes sense for CHAR_BIT > 8, so for portable code, you may
want to do it like this:

#define get8n(p) (*(p) & 0xFF)
#define get16(p) ((p)+=2, get8n((p)-2) + get8n((p)-1)<<

(untested, "n" stands for non-modifying)

>
> int main (void)
> {
> unsigned char *ptr, arr[]={255,255};
>
> ptr = arr;
>
> printf("%u\n",get16(ptr));
>
> return 0;
> }
>
>
> Cheers
> Michael



--
E-Mail: Mine is a gmx dot de address.

 
Reply With Quote
 
Mark L Pappin
Guest
Posts: n/a
 
      01-04-2005
Michael Mair <(E-Mail Removed)> writes:

> Stephen Sprunk wrote:

....
>> 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.

....
>> However, I'm not comfortable both swapping out the macros _and_
>> rewriting hundreds of calls to each at the same time


One (horribly perverted) way to emulate pass-by-reference is to do
what the author of GMP 2.0[0] did - use an array of length 1 as your
type (in that case it was done via typedef; you could do the same).

This way the calls can stay the same, but you will have to modify the
type of the objects used as second-arguments (although the compiler
will help you by pointing out any you've forgotten).

e.g.

/* struct used so we actually have new types, not just names */
typedef struct { uint16_t u; } foo16_t[1];
typedef struct { uint32_t u; } foo32_t[1];

static void in_uint16_le(struct stream *s, foo16_t v)
{
v[0].u = *(s->p++);
v[0].u += *(s->p++) << 8;
}
static void in_uint32_le(struct stream *s, foo32_t v)
{
v[0].u = *(s->p++);
v[0].u += *(s->p++) << 8;
v[0].u += *(s->p++) << 16;
v[0].u += *(s->p++) << 24;
}

There may also be some point in replacing
*(s->p++) with (*(s->p++)&0xff)
and replacing
+= with |=
in the above.

> Replacing in_uintN_t(s,v) by in_uintN_t(s,&(v)) is easier.


This approach doesn't even require that.

Not that I'm recommending it, either, because pass-by-reference
Doesn't Happen in C functions, but since your code already does it
with macros-that-look-like-functions (and aren't named in UPPER CASE
to make their nature obvious) this avoids one extra class of pain.

mlp

[0] GNU Multiple Precision arithmetic library
Version 1 didn't mess with the programmer's mind so much; the
"array of 1" hack was introduced with 2.0
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      01-05-2005
"Stephen Sprunk" <(E-Mail Removed)> 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++) <<
/* 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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
macros-loop? calling macros X times? Andrew Arro C Programming 2 07-24-2004 09:52 AM
Explanation of macros; Haskell macros mike420@ziplip.com Python 80 11-07-2003 02:22 AM
Re: Explanation of macros; Haskell macros Michael T. Babcock Python 0 11-03-2003 01:54 PM
Re: Explanation of macros; Haskell macros mike420@ziplip.com Python 5 11-01-2003 01:09 AM
Re: Explanation of macros; Haskell macros mike420@ziplip.com Python 1 10-07-2003 04:07 PM



Advertisments