2

I have a C function which calls other functions, each function returns int, where 0 is OK or error code if != 0. The function looks like:

int myfunc()
{
  int err;
  err = func1(arg1);
  if (err != 0) {
    return err;
  }
  err = func2(arg2, arg3);
  if (err != 0) {
    return err;
  }
  err = func3(arg4, arg5);
  if (err != 0) {
    return err;
  }
  err = func4();
  if (err != 0) {
    return err;
  }
  return 0;
}

As you can see I have a lot of boilerplate code:

err = some_func();
if (err != 0) {
  return err;
}

Is it possible to write some macros to simplify it? Something like:

#define TRY() ???

int myfunc()
{
  TRY(func1(arg1));
  TRY(func2(arg2, arg3));
  TRY(func3(arg4, arg5));
  TRY(func4());
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Kirill
  • 7,580
  • 6
  • 44
  • 95
  • Can you please show what you've tried so far? – Sourav Ghosh Nov 26 '19 at 10:27
  • @SouravGhosh I tried to write the macros, but it didn't compile. I'm actually not a C developer, and I don't have experience with macros writing, this is why I'm looking for help here. – Kirill Nov 26 '19 at 10:33

2 Answers2

6
#define TRY(expression)     \
  do {                      \
    int err = (expression); \
    if (err != 0) {         \
      return err;           \
    }                       \
  } while (0)

This keeps err scoped to the macro. It's legal to use the same variable name each time if it's in separate curly braced blocks.

You'll still need return 0 at the bottom.

int myfunc()
{
  TRY(func1(arg1));
  TRY(func2(arg2, arg3));
  TRY(func3(arg4, arg5));
  TRY(func4());
  return 0;
}

See also:

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
4

I would say that what you have already is good. Yes it is repetive, but it's still easy to read. Hiding jump statements, like return, behind macros is almost never a good idea.

If you insist on shortening it, you could chain function calls with logical AND or OR operators:

if((err = func1(arg1))       != 0 ||
   (err = func2(arg2, arg3)) != 0 ||
   (err = func3(arg4, arg5)) != 0 ||
   (err = func4())           != 0)
{
    return err;
}

This works because logical operators have short-circuit behaviour. When first OR condition is true, remaining conditions are not evaluated or executed.

If for some reason, like code guidelines, you cannot have assignments in if statement conditions, you can move the code chain above it:

(err = func1(arg1))       != 0 ||
(err = func2(arg2, arg3)) != 0 ||
(err = func3(arg4, arg5)) != 0 ||
(err = func4())           != 0;

if(err != 0) {
    return err;
}

Since only action that if statements have is to return error code, it can be simplified further by removing all if statements and having only one return statement at the end of the function. (Thanks to Jonathan Leiffer)

(err = func1(arg1))       != 0 ||
(err = func2(arg2, arg3)) != 0 ||
(err = func3(arg4, arg5)) != 0 ||
(err = func4())           != 0;
return err; /* Returns error code or 0. */
user694733
  • 15,208
  • 2
  • 42
  • 68
  • 1
    In the second example, since the function otherwise returns 0, there's no need to test `if (err != 0)` — simply use `return err;` after the chained assignments. – Jonathan Leffler Nov 26 '19 at 12:40
  • @JonathanLeffler That is a good point. It actually makes my first example kind of unnecessary too, if there is only return statement in the inner scope. – user694733 Nov 26 '19 at 12:47
  • 1
    In the first example, you could use: `if((err = func1(arg1)) != 0 || (err = func2(arg2, arg3)) != 0 || (err = func3(arg4, arg5)) != 0) err = func4(); return err;` which has three assigning function calls in the `if` and then has the final assigning function call as the body of the `if` — and ends up just returning `err`. In the second example, the final comparison is not used — it does make for symmetry, though. – Jonathan Leffler Nov 26 '19 at 12:51
  • @JonathanLeffler True. I think I keep the symmetry in the answer for simplicity. It shouldn't make much difference, if any, performance wise anyway. – user694733 Nov 26 '19 at 12:59