【问题标题】:Is there a way to write a C/C++ function-like macro that tests if an object-like macro is defined and generates code uses it?有没有办法编写一个类似 C/C++ 函数的宏来测试是否定义了类似对象的宏并生成使用它的代码?
【发布时间】:2021-06-28 00:58:02
【问题描述】:

我可以在网站上找到有关在宏内部使用 #if#ifdef 的其他问题,以及绕过限制以实现各种目标的方法,但它们或它们的答案似乎都不符合我的用例,这对我来说似乎很明显:

// the system flags are defined as object-like macros in this header...
#include <sys/stat.h>

// naive attempt at function-like macro that of course cannot work
#define MAYBE_EXPORT_FLAG(flag)                \
  #if defined(flag).                           \
    exports.Set(Napi::String::New(env, #flag), \
                Napi::Number::New(env, flag)); \
  #endif


Napi::Object Init(Napi::Env env, Napi::Object exports) {
    MAYBE_EXPORT_FLAG(UF_NODUMP)
    MAYBE_EXPORT_FLAG(UF_IMMUTABLE)
    MAYBE_EXPORT_FLAG(UF_APPEND)

    return exports;
}

例如,this previous question 看起来很相似,并且提供了一种解决方法,但我无法根据我的情况调整这组宏,或者如果它是可调整的,我不明白那里的某些东西。在任何情况下,它都不会在生成的代码中使用宏的参数。

我确实发现有一些技巧可以做其他一些问题想要的,但我找不到一种方法可以让它们中的任何一个适应这种情况。但它们非常棘手。那么有什么技巧还是根本不可能?

【问题讨论】:

  • 直到 C++ 有反射,最终可能会出现,如果不明确定义自己的 #define 标签,这是不切实际的。
  • @hippietrail 一个“预处理器符号”被称为宏。

标签: c++ macros c-preprocessor conditional-compilation


【解决方案1】:

您最终需要做的是为每个感兴趣的符号定义一个宏,该宏会根据是否定义符号进行扩展:

#include <sys/stat.h>

#ifdef UF_NODUMP
#define IFDEF_UF_NODUMP(...)  __VA_ARGS__
#else
#define IFDEF_UF_NODUMP(...)
#endif
#ifdef UF_IMMUTABLE
#define IFDEF_UF_IMMUTABLE(...)  __VA_ARGS__
#else
#define IFDEF_UF_IMMUTABLE(...)
#endif
#ifdef UF_APPEND
#define IFDEF_UF_APPEND(...)  __VA_ARGS__
#else
#define IFDEF_UF_APPEND(...)
#endif

#define MAYBE_EXPORT_FLAG(flag)                    \
    IFDEF_ ## flag(                                \
        exports.Set(Napi::String::New(env, #flag), \
                    Napi::Number::New(env, flag)); \
    )

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    MAYBE_EXPORT_FLAG(UF_NODUMP)
    MAYBE_EXPORT_FLAG(UF_IMMUTABLE)
    MAYBE_EXPORT_FLAG(UF_APPEND)

    return exports;
}

这有点痛苦和重复,但你可以将所有重复的东西放在它自己的头文件中,你不需要太多引用。根据您要执行的操作,您可能会发现为每个符号定义 IFNDEF_ 宏也很有用。

【讨论】:

    【解决方案2】:

    这是另一种方法,出于兴趣(请注意 - 请参阅粗体列表条目)。这不使用您的 MAYBE_EXPORT_FLAG 骨架,但是:

    • 更通用一点(基于模式匹配)
    • 使用比条件指令的巨型链更简洁(使用模式匹配检测未定义的标志)
    • 与类似标识符的标志一起使用(任何可正确粘贴到标识符的内容;包括正小数、八进制、十六进制数字)
    • 使用括号标记
    • 不适用于(非括号)“否定”标志(例如,如果您的系统将标志定义为 #define FLAG -1

    基础宏

    #define SECOND(...) SECOND_I(__VA_ARGS__,,)
    #define SECOND_I(A,B,...) B
    #define GLUE(A,B) GLUE_I(A,B)
    #define GLUE_I(A,B) A##B
    #define EAT(...)
    #define IF_NMATCHES_USEABLE(P_, F_, M_) M_
    #define IF_NMATCHES_P(...) , IF_NMATCHES_USEABLE
    #define IF_NMATCHES_CALL(P_,F_,M_) \
        SECOND(IF_NMATCHES_P F_, IF_NMATCHES_D2)(P_, F_, M_)
    #define IF_NMATCHES_D2(P_,F_,M_) \
        SECOND(GLUE(P_, F_), IF_NMATCHES_USEABLE)(P_, F_, M_)
    #define MATCHED_PATTERN(P_, F_, M_) EAT
    

    用法

    #include <sys/stat.h>
    // Match macros (prefix plus flag)
    #define STAT_FLAG_UF_NODUMP    ,MATCHED_PATTERN
    #define STAT_FLAG_UF_IMMUTABLE ,MATCHED_PATTERN
    #define STAT_FLAG_UF_APPEND    ,MATCHED_PATTERN
    // Apply 
    #define EXPORT_FLAG(FLAG_) \
       exports.Set(Napi::String::New(env, #FLAG_), \
                   Napi::Number::New(env, FLAG_));
    Napi::Object Init(Napi::Env env, Napi::Object exports) {
        IF_NMATCHES_CALL(STAT_FLAG_,UF_NODUMP,EXPORT_FLAG)(UF_NODUMP)
        IF_NMATCHES_CALL(STAT_FLAG_,UF_IMMUTABLE,EXPORT_FLAG)(UF_IMMUTABLE)
        IF_NMATCHES_CALL(STAT_FLAG_,UF_APPEND,EXPORT_FLAG)(UF_APPEND)
    
        return exports;
    }
    

    在语义上,IF_NMATCHES_CALL 将前缀、标志和类似函数的宏的名称作为参数。如果模式匹配,它将扩展为EAT,如果不匹配,则扩展为您的函数式宏的名称。然后添加调用参数; EAT 是一个可变参数宏,它吞下这些参数并扩展为空……你的宏做你想做的事。

    如果存在“模式匹配”宏,则匹配模式;模式匹配宏的名称与您检查的前缀和标志相同,替换列表为 ,MATCHED_PATTERN(逗号很重要,见下文)。

    机制

    这里的核心通过间接SECOND 宏使用C 预处理器模式匹配。默认情况下,此宏扩展为它的第二个参数;当用作模式匹配器时,您将安排第一个参数以生成要匹配的标记;对于匹配情况,您可以使用该名称定义一个宏,并在该宏的替换列表中放置一个逗号,该替换列表会在一个非默认宏中移动。

    顶级宏首先对带括号的标志替换列表应用模式匹配器;在这种情况下,IF_NMATCHES_P 将扩展,移动IF_NMATCHES_USEABLE。如果标志没有展开为带括号的列表,则调用第二级IF_NMATCHES_D2。此调用匹配前缀/后缀组合(如果您的系统将标志定义为负文本,则 GLUE 将失败,例如 -1 不能正确粘贴);如果未定义您的标志,则结果将是您的匹配宏,它将在MATCHED_PATTERN 中移动,从而导致EAT。如果定义了标志,则SECOND 宏会忽略该参数,从而生成IF_NMATCHES_USEABLEIF_NMATCHES_USEABLE 扩展为 M_

    演示

    http://coliru.stacked-crooked.com/a/d151d475f2736141

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-24
      • 2020-01-09
      • 1970-01-01
      • 2022-11-24
      • 2014-08-27
      • 2012-04-28
      • 2011-04-05
      相关资源
      最近更新 更多