libcmutils 0.6.5
Multi-platform C99 utility library
Loading...
Searching...
No Matches
The CMCall convention.

Macros

#define CMUTIL_CALL_NESTED   0
 Whether CMCall may be nested inside another CMCall's arguments.
 
#define CMUTIL_CALL_SINGLE_EVAL   0
 Whether CMCall evaluates its receiver once instead of twice.
 
#define CMUTIL_CAT2__(a, b)   a ## b
 
#define CMUTIL_CALL__(a, b, ...)   (a)->b((a), ## __VA_ARGS__)
 
#define CMCall   CMUTIL_CALL__
 Method caller for this library.
 

Detailed Description

An object in this library is a struct of function pointers, and every method takes the object itself as its first argument. CMCall writes that argument for you. What the compiler supports decides two details of how it expands, and both can be overridden before including this header.

Macro Definition Documentation

◆ CMUTIL_CALL_NESTED

#define CMUTIL_CALL_NESTED   0

Whether CMCall may be nested inside another CMCall's arguments.

The portable spelling of CMCall pastes the trailing arguments with the GNU , ## VA_ARGS extension, which is what drops the comma when a method takes no arguments. An argument that is an operand of ## is not macro replaced, and by the time the expansion is rescanned CMUTIL_CALL__ is already being expanded, so a nested CMCall is left as an undeclared identifier:

// does not compile with CMUTIL_CALL_NESTED == 0 CMCall(sock, Write, CMCall(buf, GetBytes), len, 1000);

C23 and C++20 provide VA_OPT, which drops the comma without ##. The trailing arguments are then macro replaced as usual and nesting works. That spelling also stops the empty argument list of CMCall(obj, Destroy) from tripping -pedantic.

This is decided by the compiler that includes the header, not by the one that built the library: CMCall is a preprocessor macro and generates the same call either way, so the choice cannot break the ABI.

Define CMUTIL_CALL_NESTED to 1 or 0 before including this header to force either spelling - to 1 on a compiler that offers VA_OPT as an extension outside C23, or to 0 to keep the portable spelling and have an accidental nesting rejected at compile time. Left undefined, it is enabled wherever VA_OPT is known to be available.

Whether the receiver is evaluated once or twice is a separate question, see CMUTIL_CALL_SINGLE_EVAL below.

◆ CMUTIL_CALL_SINGLE_EVAL

#define CMUTIL_CALL_SINGLE_EVAL   0

Whether CMCall evaluates its receiver once instead of twice.

The plain expansion of CMCall(obj, Method) is (obj)->Method((obj)), which names the receiver twice. That is harmless for a variable and a trap for anything with a side effect:

// with CMUTIL_CALL_SINGLE_EVAL == 0 this removes two elements CMCall(CMCall(list, RemoveFront), GetName);

Binding the receiver to a temporary needs an expression that can hold a declaration, which standard C does not have. Two extensions do:

  • a GNU statement expression, available in GCC and Clang for both C and C++;
  • an immediately invoked lambda, available in any C++ compiler.

MSVC compiling C has neither, so there the receiver is still evaluated twice. Code that has to build there must keep avoiding side effects in a receiver, which is why this is worth knowing rather than forgetting.

Define CMUTIL_CALL_SINGLE_EVAL to 0 to keep the double expansion everywhere - useful to make sure code stays portable to MSVC's C mode. Forcing it to 1 where neither extension exists is a compile error rather than a silent fallback.

◆ CMUTIL_CAT2__

#define CMUTIL_CAT2__ (   a,
 
)    a ## b

Token pasting helpers, used to give each receiver temporary a name of its own so that a nested CMCall does not shadow the enclosing one.

◆ CMUTIL_CALL__

#define CMUTIL_CALL__ (   a,
  b,
  ... 
)    (a)->b((a), ## __VA_ARGS__)

A wrapper macro for CMUTIL_CALL.

◆ CMCall

#define CMCall   CMUTIL_CALL__

Method caller for this library.

This library is built on the C language, but some usage of this library is similar to object-oriented languages like C++ or Java.

Create an instance of the type CMUTIL_XX with CMUTIL_XXCreate function and call the method with member callbacks.

ie) // Create CMUTIL_XX object instance. CMUTIL_XX *obj = CMUTIL_XXCreate(); // Call method FooBar with arguments(arg1, arg2). obj->FooBar(obj, arg1, arg2); // Destroy instance. obj->Destroy(obj);

As you see instance variable used redundantly in method call, we created a macro CMCall because of this inconvenience. As a result, the below code will produce the same result with the above code. // Create CMUTIL_XX object instance. CMUTIL_XX *obj = CMUTIL_XXCreate(); // Call method FooBar with arguments(arg1, arg2). CMCall(obj, FooBar, arg1, arg2); // Destroy instance. CMCall(obj, Destroy);