gen.h

Support for C language constructions.

Statement chaining

This module exports a bunch of so-called statement chaining macros: they expect a statement right after their invocation, and moreover, an invocation of such a macro with a statement afterwards altogether form a single statement.

How can this be helpful? Imagine you are writing a macro with the following syntax:

MY_MACRO(...) { bla bla bla }

Then MY_MACRO must expand to a statement prefix, i.e. something that expects a statement after itself. One possible solution is to make MY_MACRO expand to a sequence of statement chaining macros like this:

#define MY_MACRO(...) \
    ML99_INTRODUCE_VAR_TO_STMT(int x = 5) \
        ML99_CHAIN_EXPR_STMT(printf("%d\n", x)) \
            and so on...

Here ML99_CHAIN_EXPR_STMT accepts the statement formed by ML99_CHAIN_EXPR_STMT, which in turn accepts the next statement and so on, until a caller of MY_MACRO specifies the final statement, thus completing the chain.

See

https://www.chiark.greenend.org.uk/~sgtatham/mp/ for a more involved explanation.

Defines

ML99_GEN_SYM(prefix, id)

Generates a unique identifier id in the namespace prefix.

Let FOO be the name of an enclosing macro. Then FOO_ must be specified for prefix, and id should be given any meaningful name (this makes debugging easier).

Examples

#include <metalang99/gen.h>

#define FOO(...) FOO_NAMED(ML99_GEN_SYM(FOO_, x), __VA_ARGS__)
#define FOO_NAMED(x_sym, ...) \
     do { int x_sym = 5; __VA_ARGS__ } while (0)

// `x` here will not conflict with the `x` inside `FOO`.
FOO({
    int x = 7;
    printf("x is %d\n", x); // x is 7
});

See

https://en.wikipedia.org/wiki/Hygienic_macro

Note

Two identical calls to ML99_GEN_SYM will yield different identifiers, therefore, to refer to the result later, you must save it in an auxiliary macro’s parameter, as shown in the example above.

Note

ML99_GEN_SYM is defined only if __COUNTER__ is defined, which must be a macro yielding integral literals starting from 0 incremented by 1 each time it is called. Currently, it is supported at least by Clang, GCC, TCC, and MSVC.

ML99_TRAILING_SEMICOLON(...)

Forces a caller to put a trailing semicolon.

It is useful when defining macros, to make them formatted as complete statements.

Examples

#include <metalang99/gen.h>

#define MY_MACRO(fn_name, val_ty, val) \
    inline static val_ty fn_name(void) { return val; } \
    ML99_TRAILING_SEMICOLON()

// Defines a function which always returns 0.
MY_MACRO(zero, int, 0);

Note

This macro expands to a C declaration, therefore, it can be used outside of functions too.

ML99_semicoloned(...)

Puts a semicolon after provided arguments.

Examples

#include <metalang99/gen.h>

// int x = 5;
ML99_semicoloned(v(int x = 5))

ML99_braced(...)

Puts provided arguments into braces.

Examples

#include <metalang99/gen.h>

// { int a, b, c; }
ML99_braced(v(int a, b, c;))

ML99_assign(lhs, ...)

Generates an assignment of provided variadic arguments to lhs.

Examples

#include <metalang99/gen.h>

// x = 5, 6, 7
ML99_assign(v(x), v(5, 6, 7))

ML99_assignInitializerList(lhs, ...)

A shortcut for ML99_assign(lhs, ML99_braced(...)).

ML99_assignStmt(lhs, ...)

A shortcut for ML99_semicoloned(ML99_assign(lhs, ...)).

ML99_assignInitializerListStmt(lhs, ...)

A shortcut for ML99_assignStmt(lhs, ML99_braced(...)).

ML99_invoke(f, ...)

Generates a function/macro invocation.

Examples

#include <metalang99/gen.h>

// If you are on C11.
ML99_invoke(v(_Static_assert), v(1 == 1, "Must be true"))

ML99_invokeStmt(f, ...)

A shortcut for ML99_semicoloned(ML99_invoked(f, ...)).

ML99_prefixedBlock(prefix, ...)

Generates prefix { code }.

Examples

#include <metalang99/gen.h>

// ML99_INTRODUCE_VAR_TO_STMT(int x = 5) {
//     printf("x = %d\n", x);
// }
ML99_prefixedBlock(
    v(ML99_INTRODUCE_VAR_TO_STMT(int x = 5)),
    v(printf("x = %d\n", x);))

ML99_typedef(ident, ...)

Generates a type definition.

Examples

#include <metalang99/gen.h>

// typedef struct { int x, y; } Point;
ML99_typedef(v(Point), v(struct { int x, y; }))

ML99_struct(ident, ...)

Generates a C structure.

Examples

#include <metalang99/gen.h>

// struct Point { int x, y; }
ML99_struct(v(Point), v(int x, y;))

ML99_anonStruct(...)

Generates an anonymous C structure.

Examples

#include <metalang99/gen.h>

// struct { int x, y; }
ML99_struct(v(int x, y;))

ML99_union(ident, ...)

The same as ML99_struct but generates a union.

ML99_anonUnion(...)

The same as ML99_anonStruct but generates a union.

ML99_enum(ident, ...)

The same as ML99_struct but generates an enumeration.

ML99_anonEnum(...)

The same as ML99_anonStruct but generates an enumeration.

ML99_times(n, ...)

Pastes provided arguments n times.

Examples

#include <metalang99/gen.h>

// ~ ~ ~ ~ ~
ML99_times(v(5), v(~))

ML99_repeat(n, f)

Invokes f n times, providing an iteration index each time.

Examples

#include <metalang99/gen.h>
#include <metalang99/util.h>

// _0 _1 _2
ML99_repeat(v(3), ML99_appl(v(ML99_cat), v(_)))

ML99_indexedParams(type_list)

Generates \((T_0 \ \_0, ..., T_n \ \_n)\).

If type_list is empty, this macro results in (void).

Examples

#include <metalang99/gen.h>

// (int _0, long long _1, const char * _2)
ML99_indexedParams(ML99_list(v(int, long long, const char *)))

// (void)
ML99_indexedParams(ML99_nil())

ML99_indexedFields(type_list)

Generates \(T_0 \ \_0; ...; T_n \ \_n\).

If type_list is empty, this macro results in emptiness.

Examples

#include <metalang99/gen.h>

// int _0; long long _1; const char * _2;
ML99_indexedFields(ML99_list(v(int, long long, const char *)))

// ML99_empty()
ML99_indexedFields(ML99_nil())

ML99_indexedInitializerList(n)

Generates \(\{ \_0, ..., \_{n - 1} \}\).

If n is 0, this macro results in { 0 }.

Examples

#include <metalang99/gen.h>

// { _0, _1, _2 }
ML99_indexedInitializerList(v(3))

// { 0 }
ML99_indexedInitializerList(v(0))

ML99_indexedArgs(n)

Generates \(\_0, ..., \_{n - 1}\).

If n is 0, this macro results in emptiness.

Examples

#include <metalang99/gen.h>

// _0, _1, _2
ML99_indexedArgs(v(3))

// ML99_empty()
ML99_indexedArgs(v(0))

ML99_INTRODUCE_VAR_TO_STMT(...)

A statement chaining macro which introduces several variable definitions to a statement right after its invocation.

Variable definitions must be specified as in the first clause of the for-loop.

Top-level break/continue inside a user-provided statement are prohibited.

Example

#include <metalang99/gen.h>

for (int i = 0; i < 10; i++)
    ML99_INTRODUCE_VAR_TO_STMT(double x = 5.0, y = 7.0)
        if (i % 2 == 0)
            printf("i = %d, x = %f, y = %f\n", i, x, y);

ML99_INTRODUCE_NON_NULL_PTR_TO_STMT(ty, name, init)

The same as ML99_INTRODUCE_VAR_TO_STMT but deals with a single non-NULL pointer.

In comparison with ML99_INTRODUCE_VAR_TO_STMT, this macro generates a little less code. It introduces a pointer to ty identified by name and initialised to init.

Top-level break/continue inside a user-provided statement are prohibited.

Example

#include <metalang99/gen.h>

double x = 5.0, y = 7.0;

for (int i = 0; i < 10; i++)
    ML99_INTRODUCE_NON_NULL_PTR_TO_STMT(double, x_ptr, &x)
        ML99_INTRODUCE_NON_NULL_PTR_TO_STMT(double, y_ptr, &y)
            printf("i = %d, x = %f, y = %f\n", i, *x_ptr, *y_ptr);

Note

Unlike ML99_INTRODUCE_VAR_TO_STMT, the generated pointer is guaranteed to be used at least once, meaning that you do not need to suppress the unused variable warning.

Note

init is guaranteed to be executed only once.

ML99_CHAIN_EXPR_STMT(expr)

A statement chaining macro which executes an expression statement derived from expr right before the next statement.

Top-level break/continue inside a user-provided statement are prohibited.

Example

#include <metalang99/gen.h>

int x;

for(;;)
    ML99_CHAIN_EXPR_STMT(x = 5)
        ML99_CHAIN_EXPR_STMT(printf("%d\n", x))
            puts("abc");

ML99_CHAIN_EXPR_STMT_AFTER(expr)

The same as ML99_CHAIN_EXPR_STMT but executes expr after the next statement.

ML99_SUPPRESS_UNUSED_BEFORE_STMT(expr)

A statement chaining macro which suppresses the “unused X” warning right before a statement after its invocation.

Top-level break/continue inside a user-provided statement are prohibited.

Example

#include <metalang99/gen.h>

int x, y;

for(;;)
    ML99_SUPPRESS_UNUSED_BEFORE_STMT(x)
        ML99_SUPPRESS_UNUSED_BEFORE_STMT(y)
            puts("abc");

Deprecated:

Use ML99_CHAIN_EXPR_STMT((void)expr) instead.