0

Is it possible to do a nullity check and an access in a macro?

Eg:

#define LOG(mystruct, severity, format, ...) ({ \
  severity_t current = ERROR; \
  if (mystruct) { \
    current = mystruct->error_level; \
  } \
  if (severity >= current) { \
   ... //handle logging 
  } \
})

If I call this with LOG(NULL, DEBUG, "test %s", "one"); I get an error as such:

error: member reference base type 'void' is not a structure or union note: expanded from macro 'LOG' current = mystruct->error_level;

mystruct is defined as:

typedef struct mystruct_t {
  severity_t error_level;
}

I want to allow the possibility of working with a NULL mystruct. Eg: case of when there is an error creating the structure itself.

bge0
  • 901
  • 2
  • 10
  • 25
  • `NULL->error_level` is not compilable. Perhaps you could use `MYSTRUCT *ptr = (severity);` earlier on, and then `if ( ptr ) current = ptr->error_level;` – M.M Sep 25 '15 at 21:39
  • An inline function would be tidier – M.M Sep 25 '15 at 21:40
  • Let me clarify the structures a little. `mystruct_t` is a superstructure in that it contains a `severity_t`. I want to allow the possibility to handle a NULL for `mystruct` (eg: when there is say an error during the creation of a `mystruct_t` itself) – bge0 Sep 25 '15 at 21:43
  • And can't inline: variadic params – bge0 Sep 25 '15 at 21:45
  • You can inline variadic params, and my suggested code allows `NULL` to be used as argument – M.M Sep 25 '15 at 21:46

2 Answers2

3

Your problem is that although the first branch will never been taken, NULL hasn't the correct type to do a ->error_level.

You can avoid that by giving it the right type. I would do that with a local variable, not a cast so you'd capture wrong use cases of your macro. Just add

yourType* myStr = mystruct;
current = myStr->error_level;

and you should be fine.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
0

Is it possible to do a nullity check and an access in a macro?

Nope, the preprocessor is doing simple text replacement. It doesn't support conditionals inside the definition of a macro.

When you use the macro with LOG(NULL, DEBUG, "test %s", "one");, the fourth line expands to

current = NULL->error_level;

and since NULL is typically defined as #define NULL ((void *)0), that further expands to

current = ((void *)0)->error_level;

which is why you get the message about void not being a structure or union.


To fix the problem, don't pass NULL to the macro, pass the pointer that contains NULL to the macro, e.g.

mystruct_t *ptr = malloc(...);
if ( !ptr )
    LOG( ptr, DEBUG, "no memory" );
user3386109
  • 34,287
  • 7
  • 49
  • 68