views:

104

answers:

4

In the following code, whatever is passed as retval is evaluated as given for every use of that token.

#define _CPFS_RETURN(commit, retval) do { \
        util_cpfs_exit(commit); \
        return retval; \
    } while (false)

#define CPFS_RETURN_BOOL(retval) do { \
        _CPFS_RETURN(retval, retval); \
    } while (false)

For example given the use CPFS_RETURN_BOOL(inode && file_truncate(inode, len));, this is generated:

do { 
    do {
        util_cpfs_exit(inode && file_truncate(inode, len));
        return inode && file_truncate(inode, len);
    } while (0);
} while (0);

Evidently I don't want to execute the statement inode && file_truncate(inode, len); more than once. How can I ensure that the given tokens are evaluated before being pasted helter-skelter?

Update

I believe I have good reason to use macros here. Where possible, code is put into real functions (such as *util_cpfs_exit*) which are invoked from a set of macros I'm using. The macros vary based on the return type: in C++ I'd have explicit templates to handle this.

+1  A: 

Write a function instead of using a macro. In this case, where you want to build a return statement in, you might be better off just writing the code explicitly instead of relying on a macro to hide what you're doing.

Carl Norum
+2  A: 

I would recommend that you evaluate the condition first.

i.e.

bool val = inode && file_truncate(inode, len);

Other than that may advice would be to steer well clear of macros, they seem unnecessary in this instance, use functions instead.

radman
Downvoted for being incorrect: kriss' answer is simple and uses macros directly.
Jack Kelly
criticism accepted and answer updated for correctness.
radman
It is better, downvote removed.
Jack Kelly
+2  A: 

As your macro vary on the return type, you can evaluate the retval expression and store it in a variable of the right type inside the first level of macro then use this variable. ie:

#define CPFS_RETURN_BOOL(retval) do { \
    bool _tmp_ = retval;
    _CPFS_RETURN(_tmp_, _tmp_); \
} while (false);

If I understand well, that should be enough for your use case, and for other use cases you can use functions.

In your exemple you'll get:

do {
   bool _tmp_ = inode && file_truncate(inode, len);
   do {
      util_cpfs_exit(_tmp_);
      return _tmp_;
   } while (0);
} while (0);

Looks fine.

PS: as a sidenote if you always use _CPFS_RETURN indirectly through another macro following the above model, there is no need to protect it by a do { } while (false);. Also, putting a semi-colon after the while(false) removes most of the interest of using it... that may be a good example of why C macros are dangerous and hides easy pitfalls. Not that I dislike macros, quite the contrary. I'm from the (probably rare) kind of people that would prefer C macros to be enhanced to bypass their current limitations to become really cool (and no, C++ templates are not enhanced macros, they are something completely different).

kriss
Just be careful that `retval` isn't an expression that happens to involve a variable already named `_tmp_`.
jamesdlin
@jamesdlin: Can you provide a workaround for this miniscule possibility? I'm currently using `-Wshadow` but if there's a way to guarantee a unique variable name that's still accessible i'm all ears.
Matt Joiner
kriss
@kriss: No, it is a problem if `retval` expands to something containing `_tmp_`, you'd end up with `bool _tmp_ = ... _tmp_ ...`, and that's self-referential. I don't know of any workaround.
jamesdlin
@jamesdlin: OK, it is indeed self-reference. At first sight I thought it was some bogus scope rule in C standard, but thinking it over it can be useful with pointers to write things like `void * p = ` or such. I couldn't think of any use case where getting the actual value of a variable would be useful in an self-referential construct.
kriss
A: 

Change the macro to a "static inline" function. In gcc, it's as fast as a macro. http://gcc.gnu.org/onlinedocs/gcc/Inline.html

fseto
Please read my update
Matt Joiner
I did...However, please consider all the responses trying to convince you otherwise.
fseto