These macros exist to allow the same code to compile in two configurations. If the controlling macro DISABLE_ERS
is not defined, then ers_entry_size(some_o)
will expand to this:
((some_o)->entry_size(some_o))
Everywhere ers_entry_size(some_o)
is used in code. But the same code has to build successfully even when we define DISABLE_ERS
. Those macros could thoretically be defined as
#define ers_alloc(obj,type) ((type *)aMalloc(sizeof(type)))
#define ers_free(obj,entry) (aFree(entry))
#define ers_entry_size(obj) ((size_t)0)
#define ers_destroy(obj) (void)0)
#define ers_chunk_size(obj,size) ((size_t)0)
But what if ers_entry_size(some_o)
was the only way that a particular function used some_o
? If the macro expands to ((size_t)0)
, now some_o
becomes unused. Modern compilers emit warnings about unsed variables. We can't have our code compile to a bunch of warnings in a valid configuration of the library. That's bad library design.
So instead of the naive "no-op" definition, a smarter one is used. Since the comma operator evaluates each operand and discards all but the last, we can leverage it. When the macro is defined like you noted, ers_entry_size(some_o)
will expand to
((void)(some_o), (size_t)0)
The first expression fed to the comma operator evaluates and discards some_o
. It's being used now, so there is no warning. The second expression evaluates to the constant, and is the value of the whole parenthesized expression.