簡體   English   中英

C99的預處理器中的static_if

[英]static_if in C99's preprocessor

是否可以在C99中實現static_if?

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

如何在這里正確實現STATIC_IF(…) 根據COND ,參數應該傳遞給MACRO1MACRO2 ,但兩個宏的參數看起來不同。 COND是靜態可測試的,類似sizeof (…) > 42

  • #if COND然后#define STATIC_IF MACRO1 ...對我的用例不起作用。
  • 我不能使用編譯器特定的解決方案。

在您的具體情況下(如果我理解您的評論正確),是的,您可以這樣做。

您無法將sizeof傳遞給預處理器中的任何內容,因為預處理器在類型信息可用之前運行。 幸運的是,你不需要sizeof來計算靜態寫入列表中的參數數量( XY警報! ),所以這不是障礙。

這是使用Order宏庫的一種可能的實現:

#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;
}

(這個簡單的例子通過參數的數量來索引列表 - 可能不是你想要做的,但足以得到這個想法。訂單確實提供了一系列復雜的,非評估的控制結構 - ifcondmatch等。 - 用於更復雜的決策。)

訂單非常重要:我假設您可以使用更輕便,更逼真的P99(不熟悉它)來做類似的事情。 訂單與GCC非常合作,而Clang則非常好(Clang會在深度遞歸或長循環中窒息); 是標准的,但不是所有的編譯器都是。

我不這么認為,不是你的意思。

但是:我會繼續前進,並相信優化編譯器會注意到條件始終為真(或錯誤)並做正確的事情,即優化測試。

您可能需要強制進行一些優化以激發編譯器執行此操作。

這是不可能的,因為sizeof(something)>42的條件對於預處理器來說不是靜態的。 預處理器純粹是文本的(原則上,除了算術)。 它不知道C或類型。

請注意, #if中的條件表達受到嚴格限制。

但是,您可以使用構建技巧。 例如,您可能有一個獨立的程序,如

 // 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);
 }

然后有一個規則就像

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

在你的Makefile等等......

因此,您的構建機制將生成適當的標頭。

如果你想要交叉編譯,事情會變得很棘手!

然后,您的標題中可能包含#include "generated-sizes.h" ,以及后面的代碼

#if SIZEOF_Foo > 42
#error cannot have such big Foo
#endif

如果您可以消除必須堅持使用C99的限制,那么自C11以來,對於該語言內置的這個問題有一個更好的解決方案:

#include <stdio.h>

void f1(float x, double y, float * z) {
  printf("inside f1\n");
}

void f2(int x, _Bool * y) {
  printf("inside f2\n");
}

#define STATIC_IF(COND, ...) _Generic(&(int[(!!(COND))+1]){ 0 }, \
    int(*)[2]: f1, \
    int(*)[1]: f2) \
  (__VA_ARGS__)


int main(void) {
  float fl;
  _Bool b;

  STATIC_IF(sizeof(double) > 4, 0.0f, 1.0, &fl);
  STATIC_IF(sizeof(double) > 128, 16, &b);
}

_Generic運算符根據類型執行編譯時選擇。 由於它基於類型進行選擇,因此它也是唯一可以接受沖突類型“參數”的語言級表達式,因為它的目的是根據輸入解析正確類型的值。

這意味着您可以輕松地使用它來選擇具有不兼容簽名的兩個函數,因為它將完全忽略通過匹配輸入而未選擇的函數的類型; 參數(應用於_Generic返回的任何函數)將僅針對成功匹配進行檢查。

雖然_Generic被設計為對類型而不是值進行分派,但是任何整數常量表達式都可以通過將其用作數組的大小而“變成”類型。 所以在上面的宏中我們創建了一個匿名數組(nb這不是 VLA),計數為2(對於true)或1(對於false),並針對指向該數組的指針類型進行調度以解析要使用的兩個不兼容的函數。

這肯定會在運行時減少為空,因為不僅條件是靜態的,而且替代的“執行路徑”甚至不會進行類型檢查,因此不能首先為它生成代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM