0

I have a function dangerous(GEN x) which is called frequently in my code, where GEN is a typedef. For debugging purposes, I would like to add checkSafe to all instances of this function, something like

#ifdef DEBUG
  #define dangerous(x) GEN __x = (x); if(checkSafe(__x)) dangerous(__x)
#endif

but I'm concerned that this might not work as intended. What's the right way to do something like this? The function is used too often to instrument each use individually and it is not desirable to check outside debug mode (for various reasons).

Charles
  • 11,269
  • 13
  • 67
  • 105
  • You mean `dangerous(GEN x)` in the question body, right? – Sourav Ghosh Feb 20 '17 at 09:21
  • that sounds allright to me since you made sure that you can call your macro with an expression which has to be evaluated just once. – Jean-François Fabre Feb 20 '17 at 09:21
  • I would recommend using a function instead of a macro, to avoid the need for this . What is `checkSafe` ? – M.M Feb 20 '17 at 10:02
  • Why not adding it **inside the function** instead? – barak manos Feb 20 '17 at 10:04
  • @M.M The `dangerous` function reclaims memory on the assumption that previous calls have left it in a valid state. The `checkSafe` function as its main function emits a diagnostic if the chunk of memory passed is not in a valid state but it also returns 1 if it is and 0 if not. – Charles Feb 20 '17 at 15:09
  • @barakmanos It's a somewhat complicated situation, but I can't easily do that and if I did it wouldn't do quite the same thing. – Charles Feb 20 '17 at 15:18

1 Answers1

3

Things to be aware of / careful about:

  1. Using a macro and a function with the same name at the same time. While it can produce valid C, you'll have to 1) take extra precautions to avoid unwanted expansion (either always define the function before the macro, or enclose the function name in parentheses at definition time) and 2) double check that every use of the function also includes your instrumenting code.

Solution: rename the original function into something like _dangerous.

  1. Using the macro in various situations:
    • in an if with a single statement: if (foo) dangerous(x);
    • around an else from the parent if: if (foo) dangerous(x); else bar();
    • when leaking variables into the parent namespace can break things: GEN __x = 5; dangerous(__x);.

Solution: enclose the macro in a construct like do { ... } while(0).

  1. You must take into account any side effects at copy time, like resource allocation or CPU intensive operations (since GEN is a typedef, this is likely not a concern).

Lastly, you may also want to complain when checkSafe fails, e.g. by logging an error message, or even aborting the program.

Putting the above together, you would instrument the function like this:

#ifdef DEBUG
  #define dangerous(x) do { \
    GEN __x = (x);          \
    if (checkSafe(__x))     \
      _dangerous(__x);      \
    else                    \
      complainAbout(__x);   \
  } while(0)
#else
  #define dangerous _dangerous
#endif
  1. If dangerous() returns a value (e.g. int) that you want to use.

Solution: Define a function to instrument your original function and pass the return value up:

#ifdef DEBUG
  static inline int dangerous(GEN x) {
    if (checkSafe(x))
      return _dangerous(x);
    complainAbout(x);
    return ERROR_CODE;
  }
#else
  #define dangerous _dangerous
#endif
Sir Athos
  • 9,403
  • 2
  • 22
  • 23
  • 1
    note: if using `inline` in C, [read this](http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99). The behaviour is unintuitive – M.M Feb 20 '17 at 20:23
  • Thanks! I added `static` to the definition. – Sir Athos Feb 20 '17 at 21:30