🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: comp.lang.c.moderated
8 messages
8 total messages Started by Antony Pace (UK/CH) Sat, 23 Mar 1996 06:43
Macros and Braces
#2355
Author: Antony Pace (UK/CH)
Date: Sat, 23 Mar 1996 06:43
126 lines
3495 bytes
Hi all,

I am having problems writing a macro (Ansi C) for which I feel there
must be standard technique.  Can anyone help?


Here is my first attempt at the macro.  I have defined an error type
which is a small structure with 3 variables in it.  I want to use a
macro to clear all the variables so that if the number or type of
variables change, only my macro has to change.

typedef struct {
  int a;
  int b;
  int c;
} t_error;


#define CLEAR_ERROR(e) { \
  (e).a = 0;             \
  (e).b = 0;             \
  (e).c = 0;             \
  TRACE("clear error");  \
}


TRACE is a macro provided by another module which is approximately:

#define TRACE(s)                                    \
  if (trace_on)                                     \
    fprintf(trace_file, __FILE__ __LINE__ "%s", s);

Now, when I use my CLEAR_ERROR macro in code it works ok.

void my_fn(void) {
  t_error error;

  CLEAR_ERROR(error);
  ...
}

Note how the semicolon I put after the invocation becomes a null
statement.  I expect users of my CLEAR_ERROR macro to use it, as then
the macro looks like a function call.

In this code segment, my macro does not work:

  ...
  if (a==3)
    CLEAR_ERROR(error);
  else
    b=5;
  ...


This expands to:

  ...
  if (a==3) {
    (e).a = 0;
    (e).b = 0;
    (e).c = 0;
    TRACE("clear error");
  }
  ;   /* note the null statement */
  else
    b=5;
  ...

Of course I get an error 'else' has no 'if'.
----------------------------------------------------

Second part!

Now normally in this situation I would either combine the macro
statements to make a single statement by using the comma expression, or
I would call a function from the macro.


The comma expression
--------------------

#define CLEAR_ERROR(e)  \
  (e).a = 0, (e).b = 0 ,  (e).c = 0 , TRACE("clear error")

which expands to 

  (e).a = 0, (e).b = 0 ,  (e).c = 0 ,  if (trace_on)
    fprintf(trace_file, __FILE__ __LINE__ "%s", "clear error")
  ;

The semicolon is now a statement separator and not a null statement, but
the TRACE expands to an if statement which does not work with the comma
expression.

The function call
-----------------

#define CLEAR_ERROR(e) \
  clear_error(e)

void clear_error(t_error error) {
  (error).a = 0;            
  (error).b = 0;           
  (error).c = 0;           
  TRACE("clear error");
}

But not here what the TRACE puts in the trace file for the __FILE__ and
__LINE__ numbers.  Of course it is always the same file and line.

I could just put the variable clearing in the function but then I still
have two statements in the macro and that is no better than having four.

My current implementation looks like this.

#define CLEAR_ERROR(e) \
  if (!((e).a = (e).b = (e).c = 0)) TRACE("clear error")

It uses an unnecessary comparison which I don't like but at least it
works!

As I said at the top, I am sure that I am not the first with this
problem and would love to hear of THE clean solution.  Thanks very much.

Ant
Re: Macros and Braces
#2367
Author: S. Lee
Date: Sun, 24 Mar 1996 17:40
44 lines
1420 bytes
In article <4j06iu$7qm@solutions.solon.com>,
Antony Pace (UK/CH) <100576.1776@compuserve.com> wrote:
>Hi all,
>
>I am having problems writing a macro (Ansi C) for which I feel there
>must be standard technique.  Can anyone help?
>
>#define CLEAR_ERROR(e) { \
>  (e).a = 0;             \
>  (e).b = 0;             \
>  (e).c = 0;             \
>  TRACE("clear error");  \
>}

You can use

do {
  (e).a = 0;
  ...
} while (0)

for this macro.

>TRACE is a macro provided by another module which is approximately:
>
>#define TRACE(s)                                    \
>  if (trace_on)                                     \
>    fprintf(trace_file, __FILE__ __LINE__ "%s", s);

if (!trace_on) {
} else
  fprintf(trace_file, ...)

or

do {
  if (trace_on)
    fprintf(trace_file, __FILE__ __LINE__ "%s", s);
} while (0)

This will let you use the macros in a if statement without fear of "else"
getting the wrong "if" (or not getting one at all).

Stephen
Re: Macros and Braces
#2369
Author: Stig Hemmer
Date: Sun, 24 Mar 1996 17:41
75 lines
2958 bytes
In article <4j06iu$7qm@solutions.solon.com> 100576.1776@compuserve.com (Antony Pace (UK/CH)) writes:
[How can I write a macro that works like a function in all contexts?]

There are many ways, here are some variants of your methods:
> #define CLEAR_ERROR(e) \
>   clear_error(e)
> 
> void clear_error(t_error error) {
>   (error).a = 0;            
>   (error).b = 0;           
>   (error).c = 0;           
>   TRACE("clear error");
> }

Try:
#define CLEAR_ERROR(e) \
  clear_error(e,__FILE__,__LINE__)

You'll need another macro TRACE2 which accepts file and line arguments
but this is just a minor rewrite of TRACE. And you can do

#define TRACE(s) TRACE2(s,__FILE__,__LINE__)

If this is something you do often, you probably don't want the
function call overhead though...

>#define CLEAR_ERROR(e)  \
>  (e).a = 0, (e).b = 0 ,  (e).c = 0 , TRACE("clear error")
>
>which expands to 
>
>  (e).a = 0, (e).b = 0 ,  (e).c = 0 ,  if (trace_on)
>    fprintf(trace_file, __FILE__ __LINE__ "%s", "clear error")
>  ;
>
>The semicolon is now a statement separator and not a null statement, but
>the TRACE expands to an if statement which does not work with the comma
>expression.

Rewrite TRACE as an expression too and it will work:
#define TRACE (trace_on && fprintf(...))


> #define CLEAR_ERROR(e) \
>   if (!((e).a = (e).b = (e).c = 0)) TRACE("clear error")

> It uses an unnecessary comparison which I don't like but at least it
> works!

Never mind the comparison, the compiler will probably optimize it away
anyway. Another similar way of doing this:

#define CLEAR_ERROR(e)\
   do {\
      ...\
   } while(0)

Again a good compiler should kill the comparison. In this version
"..." can be anything, not just expressions.

>As I said at the top, I am sure that I am not the first with this
>problem and would love to hear of THE clean solution.  Thanks very much.

As you can see, there are many ways of solving this problem. The last
one is IMHO closest to being THE solution since it can be used
regardless what you want to do in the macro. In fact, I have a feeling
that this is the 'traditional' way of solving this problem, though I
can't really remember where I learnt it first.

Happy hacking!

Stig Hemmer   aka   stig@pvv.unit.no
pvv   - ProgramVareVerkstedet ("The Software Workshop", a student society)
.unit - UNiversitetet I Trondheim
.no   - NOrway, a minor kingdom in northern Europe.           Yes in 2016!
Re: Macros and Braces
#2375
Author: Roger Ramhoff
Date: Sun, 24 Mar 1996 17:48
43 lines
1555 bytes
Antony Pace (UK/CH) wrote:
> 
> Can anyone help?
> 
> Here is my first attempt at the macro.
> 
> #define CLEAR_ERROR(e) { \
>   (e).a = 0;             \
>   (e).b = 0;             \
>   (e).c = 0;             \
>   TRACE("clear error");  \
> }
> 
> In this code segment, my macro does not work:
> 
>   ...
>   if (a==3)
>     CLEAR_ERROR(error);
>   else
>     b=5;

Well, I'm not much of a theoretician, so perhaps my answer won't count
for much.

Anyway, if I were you, I'd publish that the call of the CLEAR_ERROR(e)
macro (especially as an if/else clause) should either

    a)  always be within braces -- { CLEAR_ERROR (e) }
or
    b)  never be followed by a semicolon --  CLEAR_ERROR (e)

My personal style choice would be 'a'.  I use that style so that I can
always expand an if/else clause without forgetting to add a brace.  I
just plain don't care that it may add extra white space.

Just my 2c.
-- 

Roger Ramhoff
It *can* be better

Field Consultant			William.R.Ramhoff@cdc.com
Control Data Systems			(810) 262-8235
Re: Macros and Braces
#2376
Author: Kurt Watzka
Date: Sun, 24 Mar 1996 17:48
53 lines
1940 bytes
100576.1776@compuserve.com (Antony Pace (UK/CH)) writes:

>Hi all,

>I am having problems writing a macro (Ansi C) for which I feel there
>must be standard technique.  Can anyone help?

[some text edited]

>#define CLEAR_ERROR(e) { \
>  (e).a = 0;             \
>  (e).b = 0;             \
>  (e).c = 0;             \
>  TRACE("clear error");  \
>}

[example of a situation where "CLEAR_ERROR(foo);" works edited]

>Note how the semicolon I put after the invocation becomes a null
>statement.  I expect users of my CLEAR_ERROR macro to use it, as then
>the macro looks like a function call.

>In this code segment, my macro does not work:

>  ...
>  if (a==3)
>    CLEAR_ERROR(error);
>  else
>    b=5;
>  ...

This is why "function like" macros should not be defined using "raw"
braces. A useful solution is to put the "body" of the macro in a
"do { ... } while(0)" loop.
 
#define CLEAR_ERROR(e) do { \
  (e).a = 0;             \
  (e).b = 0;             \
  (e).c = 0;             \
  TRACE("clear error");  \
} while (0)

Now you _need_ a semicolon after the "invocation" of the macro. I would
not dare to claim that this is a "standard technique", but the only
potential problem I see is that you have to persuade lint that you
_really_ want a constant conditional expression in that while statement.

Kurt
-- 
| Kurt Watzka                             Phone : +49-89-2180-6254
| watzka@stat.uni-muenchen.de
| ua302aa@sunmail.lrz-muenchen.de
Re: Macros and Braces
#2381
Author: news
Date: Sun, 24 Mar 1996 17:50
28 lines
1313 bytes
In article <4j06iu$7qm@solutions.solon.com>,
Antony Pace (UK/CH) <100576.1776@compuserve.com> wrote:
 >I am having problems writing a macro (Ansi C) for which I feel there
 >must be standard technique.  Can anyone help?
 >
 >Here is my first attempt at the macro.  I have defined an error type
 >which is a small structure with 3 variables in it.  I want to use a
 >macro to clear all the variables so that if the number or type of
 >variables change, only my macro has to change.
 >
 >typedef struct {
 >  int a;
 >  int b;
 >  int c;
 >} t_error;
 >
 >#define CLEAR_ERROR(e) { \
 >  (e).a = 0;             \
 >  (e).b = 0;             \
 >  (e).c = 0;             \
 >  TRACE("clear error");  \
 >}

what about: 
#define CLEAR_ERROR(e) \
  ( (e).a = (e).b = (e).c = 0, TRACE( "clear error" ) )
where TRACE(s) is:
  #define TRACE(s)  ( traceon ? fprintf( ... ) : 0 )
Re: Macros and Braces
#2382
Author: Thad Smith
Date: Sun, 24 Mar 1996 17:51
61 lines
2383 bytes
In article <4j06iu$7qm@solutions.solon.com>,
100576.1776@compuserve.com (Antony Pace (UK/CH)) wrote:
 >I am having problems writing a macro (Ansi C) for which I feel there
 >must be standard technique.  Can anyone help?
...
 >#define CLEAR_ERROR(e) { \
 >  (e).a = 0;             \
 >  (e).b = 0;             \
 >  (e).c = 0;             \
 >  TRACE("clear error");  \
 >}
...
 >#define TRACE(s)                                    \
 >  if (trace_on)                                     \
 >    fprintf(trace_file, __FILE__ __LINE__ "%s", s);
...
 >In this code segment, my macro does not work:
 >
 >  ...
 >  if (a==3)
 >    CLEAR_ERROR(error);
 >  else
 >    b=5;
 >  ...

The clever trick for this is to enclose the multiple statements within 
    do { /* statements here */ }while(0)
bracketing.  This avoids the problem you found with braces.

 >Now normally in this situation I would either combine the macro
 >statements to make a single statement by using the comma expression, or
 >I would call a function from the macro.
 >
 >
 >The comma expression
 >--------------------
 >
 >#define CLEAR_ERROR(e)  \
 >  (e).a = 0, (e).b = 0 ,  (e).c = 0 , TRACE("clear error")
 >
 >which expands to 
 >
 >  (e).a = 0, (e).b = 0 ,  (e).c = 0 ,  if (trace_on)
 >    fprintf(trace_file, __FILE__ __LINE__ "%s", "clear error")
 >  ;
 >
 >The semicolon is now a statement separator and not a null statement, but
 >the TRACE expands to an if statement which does not work with the comma
 >expression.

The situation applies to TRACE() as well as CLEAR_ERROR().  If TRACE()
were defined as

  #define TRACE(msg) (void)(trace_on? fprintf(...):0)

you could use it in a comma expression for CLEAR_ERROR.  The semicolon
in the current definition is a potential problem of the type you found
with CLEAR_ERROR().  I often add the void cast to a function-like
macro that does not return any meaningful value.

Thad
Re: Macros and Braces
#2394
Author: Richard Krehbiel
Date: Wed, 27 Mar 1996 00:59
35 lines
1666 bytes
100576.1776@compuserve.com (Antony Pace (UK/CH)) wrote:

[...much deleted...]

>The comma expression
>--------------------

>#define CLEAR_ERROR(e)  \
>  (e).a = 0, (e).b = 0 ,  (e).c = 0 , TRACE("clear error")

>which expands to 

>  (e).a = 0, (e).b = 0 ,  (e).c = 0 ,  if (trace_on)
>    fprintf(trace_file, __FILE__ __LINE__ "%s", "clear error")
>  ;

>The semicolon is now a statement separator and not a null statement, but
>the TRACE expands to an if statement which does not work with the comma
>expression.

If you can rewrite the TRACE macro, you can change it from a
"statement" to an "expression" by using the short-circuiting
properties of the && operator as follows:

#define TRACE(msg) (void) (trace_on && \
   fprintf(trace_file, "%s[%d]: %s", __FILE__, __LINE__, msg))

....and then your comma-expression version of CLEAR_ERROR will work.
In fact, this also makes your comma-expression version into an
"expression", rather than a "statement" as it would be using the
"while" trick, which may (or may not) be desirable.

--
Richard Krehbiel, Kastle Systems, Arlington VA USA
rich@kastle.com (work) or richk@mnsinc.com (personal)
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads