6

In my code i have a lot of code like:

if (block) block(....)

So I want to define a macro, something like

#define safetyCall(block, ...) if((block)) {block(##__VA_ARGS__)};

But i couldn't get it to work. Any idea?

jAckOdE
  • 2,402
  • 8
  • 37
  • 67
  • if i call `safetyCall(method, 0, newErr);` compiler say: "Pasting formed '(0', an invalid preprocessing token" and "Expected';' after expression" – jAckOdE Oct 01 '13 at 06:11

2 Answers2

6

You don't need the ## and the ; needs moving:

#define safetyCall(block, ...) if((block)) { block(__VA_ARGS__); }
trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • btw, what diff between `__VA_ARGS__` and `##__VA_ARGS__` – jAckOdE Oct 01 '13 at 06:15
  • 1
    @jAckOdE The `##` token is used to "stringify" it's arguments (i.e. turn them from `x` into `"x"`). – trojanfoe Oct 01 '13 at 06:17
  • 1
    @trojanfoe, not quite. `##` is for token pasting. `#` is to stringify. That's part of the what original error is saying. – Ken Thomases Oct 01 '13 at 06:24
  • @KenThomases Yep - true. – trojanfoe Oct 01 '13 at 07:52
  • 1
    @KenThomases: Not quite in this case. `##` when used before a `__VA_ARGS__` is a GCC extension causing the previous comma to be removed if the `__VA_ARGS__` is empty: https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html It is not necessary in this case since the `__VA_ARGS__` is the entire argument list; but in the case of `block(something, __VA_ARGS__)`, it would fail if the varargs is empty, in which case he would need `block(something, ##__VA_ARGS__)` – newacct May 05 '15 at 19:23
  • @newacct, you'll notice that document you linked refers to `##` as the token paste operator, which is what I said. Also, the original error (in the OP's comment on his question) referenced the results of token pasting. – Ken Thomases May 06 '15 at 06:00
  • @KenThomases: I know that `##` is normally the token paste operator, but it has an additional separate meaning in GCC when used between a comma and `__VA_ARGS__`, and when someone puts `##` in front of `__VA_ARGS__`, that other meaning is what they are getting at. – newacct May 06 '15 at 09:32
  • It's not separate. It's just a specialization of token pasting that absorbs a leading comma. Nothing that you've said contradicts my comment that you are apparently replying to. And I repeat: the **preprocessor** itself is considering `##` before `__VA_ARGS__` to still be token pasting. It says so right in its error message. – Ken Thomases May 06 '15 at 09:44
  • Should this be in a `do { ... } while(0)`? – Ky - May 26 '16 at 18:40
  • @BenC.R.Leggiero Why do you say that? – trojanfoe May 26 '16 at 21:40
  • I often see macros placed in a `do { ... } while(0)` for scope-safety. See also [Why use apparently meaningless do-while and if-else statements in C/C++ macros?](http://stackoverflow.com/q/154136/4479896) – Ky - Jun 06 '16 at 13:46
  • @BenC.R.Leggiero the `do { ... } while(0)` is for the multi-line code, this call is an one-line code because of the `if(){}`. So it should be ok without `do { ... } while(0)`. – Galvin Jun 23 '16 at 03:03
2

This can run into issues if your block is inline and contains code that has a series of comma separated strings, etc.

Example:

safetyCall(^void() {
  NSArray *foo = @[@"alice", "bob"];
};

The compiler will complain about "Expected ']' or '.'" and "Expected identifier or '('".

However, if you were to declare the inline block as a separate block before the macro, it will not generate an error.

Example:

void (^fooBlock)(void) = ^void() {
  NSArray *foo = @[@"alice", @"bob"];
}

safetyCall(fooBlock);
Ari Braginsky
  • 928
  • 1
  • 11
  • 21