Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static_if in C99's preprocessor

Is it possible to implement static_if in C99?

#define STATIC_IF(COND, ...) \
     if (COND) MACRO1(__VA_ARGS__); \
     else MACRO2(__VA_ARGS__);

How can I properly implement STATIC_IF(…) in here? Depending on COND the arguments either should be passed to MACRO1 or MACRO2, but the arguments for both macros look differently. COND is statically testable, something like sizeof (…) > 42.

  • #if COND then #define STATIC_IF MACRO1 … wouldn't work for my use case.
  • I cannot use compiler specific solutions.
like image 635
kay Avatar asked Oct 14 '25 08:10

kay


2 Answers

In your specific case (if I understand your comments correctly), yes, you can do this.

You can't pass sizeof to anything in the preprocessor because the preprocessor runs before type information is available. Luckily for you, you don't need sizeof to count the number of arguments in a statically-written list (X-Y alert!), so this is no obstacle.

Here's one possible implementation using the Order macro library:

#include <stdio.h>
#include <order/interpreter.h>

void oneArg(int a) {
    printf("one arg: %d\n", a);
}

void twoArgs(int a, int b) {
    printf("two args: %d %d\n", a, b);
}

void threeArgs(int a, int b, int c) {
    printf("three args: %d %d %d\n", a, b, c);
}

#define ORDER_PP_DEF_8function_list  \
ORDER_PP_CONST(("unused")            \
               (oneArg)              \
               (twoArgs)             \
               (threeArgs))

#define SelectFunction(...) ORDER_PP (                                 \
    8seq_at(8tuple_size(8((__VA_ARGS__))), 8function_list)  \
)

#define Overloaded(...) SelectFunction(__VA_ARGS__)(__VA_ARGS__)

int main(void) {
    Overloaded(42);
    Overloaded(42, 47);
    Overloaded(42, 47, 64);
    return 0;
}

(This simple case indexes a list by the number of arguments - probably not exactly what you want to do, but enough to get the idea. Order does provide a full range of complex, nonevaluating control structures - if, cond, match, etc. - for more complex decision-making.)

Order is pretty heavyweight: I assume you can do something similar with the much lighter and more realistically-portable P99 (not familiar with it). Order works very well with GCC and adequately well with Clang (Clang will choke on deep recursion or long loops); it is standard, but not all compilers are.

like image 169
Leushenko Avatar answered Oct 17 '25 01:10

Leushenko


This is not possible, because a condition like sizeof(something)>42 is not static for the preprocessor. The preprocessor is purely textual (in principle, except for arithmetic). It does not know about C or types.

Notice that expression of the condition in #if is severely constrained.

However, you could use build tricks. For instance, you might have a standalone program like

 // generate-sizeof.c
 #include <stdio.h>
 #include "foo-header.h"

 int main(int argc, char**argv) {
    const char* headername = NULL;
    if (argc<2) 
      { fprintf(stderr, "%s: missing header name\n", argv[0]); 
        exit(EXIT_FAILURE); };
    headername = argv[1]; 
    FILE *fh = fopen(headername, "w");
    if (!fh) { perror(headername); exit(EXIT_FAILURE); };
    fprintf(fp, "// generated file %s\n", headername);
    fprintf(fp, "#define SIZEOF_charptr %d\n", (int) sizeof(char*));
    fprintf(fp, "#define SIZEOF_Foo %d\n", (int) sizeof(Foo));
    fclose (fp);
 }

then have a rule like

 generated-sizes.h : generate-sizeof foo-header.h
     ./generate-sizeof generated-sizes.h

in your Makefile etc etc...

So your build machinery will generate the appropriate headers.

Things become much tricker if you want to cross-compile!

Then you might have an #include "generated-sizes.h" in your header, and later code

#if SIZEOF_Foo > 42
#error cannot have such big Foo
#endif
like image 20
Basile Starynkevitch Avatar answered Oct 17 '25 00:10

Basile Starynkevitch