5

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.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Matt Joiner
  • 112,946
  • 110
  • 377
  • 526

4 Answers4

3

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
  • 23,497
  • 17
  • 97
  • 116
  • 1
    Just be careful that `retval` isn't an expression that happens to involve a variable already named `_tmp_`. – jamesdlin Aug 31 '10 at 05:32
  • @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 Aug 31 '10 at 11:36
  • @jamesdlin & @Matt Joiner: an already existing var with name `_tmp_` shouldn't be a problem as it would be used *before* the new `_tmp_` enter scope. The old one would be hidden afterward, but that's not a problem at all. – kriss Aug 31 '10 at 13:54
  • 1
    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 Aug 31 '10 at 17:44
  • @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 = &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 Aug 31 '10 at 19:52
1

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
  • 17,675
  • 11
  • 42
  • 58
0

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
  • 219,201
  • 40
  • 422
  • 469
-2

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
  • 9,898
  • 1
  • 19
  • 14