tags:

views:

106

answers:

4

Hello,

gcc 4.4.2 c89

I have this code snippet that I have to repeat in a lot of my code. I am just wondering is there a way to make it short by using a macro function?

There is the code I would like to change.

ERR_INFO error_info; /* create error object */
ErrorInfo(&error_info); /* pass the address for it to be filled with error info */
fprintf(stderr, "And the error is? [ %s ]\n", error_info.msg); /* display the error msg */

And my attempt to create a macro function to use it.

#define DISPLAY_ERR(error_info) ErrorInfo(&error_info) error_info.msg
fprintf(stderr, "And the error is? [ %s ]\n", DISPLAY_ERR); /* display the error

Any suggestions would be most helpful,

+1  A: 

Are you trying to create a macro which "returns" a value? In C++ you can use the comma operator , to evaluate the left expression and then return the right expression. You can do the same in C, too.

(foo(var), var.field) // foo(...)is evaluated first,
                      // then second expression is returned

also

DISPLAY(message) // Pass an argument to macro
aaa
+1  A: 

You need to make it work like a function call, so it can be used anywhere a function call can, except with no value returned. You also need to mark the ends of intermediate lines with a backslash. And the 'do { ... } while (0) idiom is useful in this context:

#define DISPLAY_ERR() do { ERR_INFO error_info; ErrorInfo(&error_info); \
     fprintf(stderr, "And the error is? [ %s ]\n", error_info.msg); } while (0)

The error_info variable is local to the block, so you don't have to remember to declare it in the functions where you use the macro (or have it as a file static or, perish the thought, global variable).

Note that this code does not return a value, but it can be used anywhere in a function that a void expression could be used:

if (somefunc() != 0)
    DISPLAY_ERR();
else if (anotherfunc() != 0)
    DISPLAY_ERR();
else
    do_something_useful_after_all();

Etc.

I would still want to make sure I measured the overhead of using a regular function versus having the function-like macro. Used often enough, you might be still be better off with a real function.

Jonathan Leffler
+6  A: 

If you really want a macro:

#define DISPLAY_ERR(error_info) \
    do \
    { \
        ErrorInfo(&(error_info)); \
        fprintf(stderr, "And the error is? [ %s ]\n", (error_info).msg); \
    } while(0)

You need the do... while(0) because of a good reason.

Then, you call your macro when you want to print the error:

if (error) {
    DISPLAY_ERR(error_info);
    /* more statements if needed */
}

I am assuming that error_info has been defined somewhere. If not, or if you don't want to, then you can change your macro definition and use:

#define DISPLAY_ERR() \
    do \
    { \
        ERR_INFO error_info;
        ErrorInfo(&error_info); \
        fprintf(stderr, "And the error is? [ %s ]\n", error_info.msg); \
    } while(0)

if (error) {
    DISPLAY_ERR();
    /* more statements if needed */
}
Alok
No extra parentheses around `error_info`?
Mike D.
@Mike: you're right, I should add the parentheses. In my defense, I wouldn't define a macro for this task myself :-)
Alok
@Alok. Why wouldn't you define a macro for this task? What criteria do you use to decide whether you would create a macro for any specific task?
robUK
@robUK: In C, I usually define macros for compile-time constants most of the time. The above macro does little except save typing. For such things, I would define a function. You aren't going to spend a lot of time printing errors, so efficiency shouldn't really be a concern. Even if it was, I would define a function anyway and then see if the function is a bottleneck.
Alok
@Alok: I wouldn't either. @rob: One of the big problems with macros is that they have no type checking on the parameters, so if you feed it the wrong type, it'll either give you a hard-to-read build error, or worse, it'll compile and seem to work... until it doesn't. (e.g. a macro that does a `scanf()` to read a `double`... but if you pass it a `float*`... oops.) `inline` functions are much better for this task.
Mike D.
Further, if you need something with the flexibility of C++ templates in C, you're better off writing a Perl (for example) script that turns a `.h.in`/`.c.in` pair of files into a set of `.h`/`.c` files and then compiles them, than writing a `.h` file that uses macro replacement.
Mike D.
+1  A: 

There's a couple of ways you could do this. You could use the comma operator:

#define DISPLAY_ERR(error_info) (ErrorInfo(&(error_info)),(error_info).msg)

...or you could change the ErrorInfo() function so that it's return value is the pointer you pass it:

#define DISPLAY_ERR(error_info) (ErrorInfo(&(error_info))->msg)

(and a few more options besides).

caf