草庐IT

c++ - 基于参数的 C 预处理器宏特化

coder 2023-06-02 原文

是否可以让一个宏对一个特定的参数值进行不同的扩展,而对所有其他参数进行不同的扩展?

假设我定义了一个当前用户:

#define CURRENT_USER john_smith

我想要做的是有一个宏,如果用户传递匹配 CURRENT_USER,它将以不同的方式扩展。请注意,我事先并不知道所有可能的用户。最基本的案例:

#define IS_CURRENT_USER(user)                   \
    /* this is not valid preprocessor macro */  \
    #if user == CURRENT_USER                    \
        1                                       \
    #else                                       \
        0                                       \
    #endif                                      

使用这样的宏,依赖用户名的所有其他宏都可以通过以下方式完成:

#define SOME_USER_SPECIFIC_MACRO(user) SOME_USER_SPECIFIC_MACRO_SWITCH_1(IS_CURRENT_USER(user))

#define SOME_USER_SPECIFIC_MACRO_SWITCH_1(switch)   SOME_USER_SPECIFIC_MACRO_SWITCH_2(switch) // expand switch ...
#define SOME_USER_SPECIFIC_MACRO_SWITCH_2(switch)   SOME_USER_SPECIFIC_MACRO_##switch         // ... and select specific case

#define SOME_USER_SPECIFIC_MACRO_0  ... // not current user
#define SOME_USER_SPECIFIC_MACRO_1  ... // current user

这可能吗?

编辑:让我澄清一下。假设每个程序员在他们的配置头中定义了不同的 CURRENT_USER。当且仅当它们的 user 参数与 CURRENT_USER 匹配时,我希望用户特定的宏扩展为有意义的东西。因为我希望这些宏包含 _pragmas,所以它不能进行运行时检查(如下面的一些回答者所建议的那样)。

编辑:再次澄清。假设有一个宏可以禁用某些代码段的优化:

#define TURN_OPTIMISATION_OFF __pragma optimize("", off)

一些程序员希望关闭不同代码段的优化,但不是一次全部关闭。我想要的是有一个宏:

#define TURN_OPTIMISATION_OFF(user) /* magic */

这将匹配 user 参数与 CURRENT_USER 宏,取自每个程序员的配置文件。如果用户匹配宏则扩展为 pragma。如果没有,那就什么都没有。

最佳答案

首先,您可以使用 ## 与预处理器进行模式匹配。这就是 IIF 宏的定义方式:

#define IIF(cond) IIF_ ## cond
#define IIF_0(t, f) f
#define IIF_1(t, f) t

但是,这种方法存在一个问题。 ## 运算符的一个微妙的副作用是它禁止扩展。举个例子:

#define A() 1
//This correctly expands to true
IIF(1)(true, false) 
// This will however expand to IIF_A()(true, false)
// This is because A() doesn't expand to 1,
// because its inhibited by the ## operator
IIF(A())(true, false) 

解决此问题的方法是使用另一种间接方式。由于这很常见,我们可以编写一个名为 CAT 的宏,它可以无限制地连接。

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

所以现在我们可以编写 IIF 宏了:

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define A() 1
//This correctly expands to true
IIF(1)(true, false) 
// And this will also now correctly expand to true
IIF(A())(true, false)

通过模式匹配,我们可以定义其他操作,例如采用补码的COMPL:

// A complement operator
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
// An and operator
#define BITAND(x) PRIMITIVE_CAT(BITAND_, x)
#define BITAND_0(y) 0
#define BITAND_1(y) y

接下来,可以使用检测技术来检测参数是某个值还是括号。它依赖于扩展为不同数量参数的可变参数。检测的核心是一个带有 PROBE 宏的 CHECK 宏,如下所示:

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,

这很简单。当探针被赋予 CHECK 宏时,如下所示:

CHECK(PROBE(~)) // Expands to 1

但如果我们给它一个标记:

CHECK(xxx) // Expands to 0

因此,我们可以创建一些检测宏。例如,如果我们想检测括号:

#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x)
#define IS_PAREN_PROBE(...) PROBE(~)
IS_PAREN(()) // Expands to 1
IS_PAREN(xxx) // Expands to 0

接下来,我们需要比较两个标记,我们可以依靠宏不递归扩展的事实。我们强制宏在另一个宏内部递归扩展。如果两个标记相同,那么它将递归地扩展宏,我们将通过尝试检测它们是否扩展为括号来检测,这里是 COMPARE 宏:

#define COMPARE(a, b) PRIMITIVE_COMPARE(a, b)
#define PRIMITIVE_COMPARE(a, b) \
    IIF( \
        BITAND \
            (IS_PAREN(COMPARE_ ## a(()))) \
            (IS_PAREN(COMPARE_ ## b(()))) \
    )( \
        COMPL(IS_PAREN( \
            COMPARE_ ## a( \
                COMPARE_ ## b \
            )(()) \
        )), \
        0 \
    ) \

您要比较的每个标记都可以这样定义:

// So you would define one for each user
#define COMPARE_john_smith(x) x
#define COMPARE_another_user_name(x) x

现在,我不完全理解您想要生成的最终输出,所以假设您有一个用于为当前用户和其他用户生成代码的宏:

#define MACRO_CURRENT_USER(user) ...
#define MACRO_OTHER_USER(user) ...

那么你可以这样写:

// Detects if its the current user
#define IS_CURRENT_USER(user) COMPARE(user, CURRENT_USER)
// Your macro
#define MACRO(user) IIF(IS_CURRENT_USER(user))(MACRO_CURRENT_USER, MACRO_OTHER_USER)(user)

关于c++ - 基于参数的 C 预处理器宏特化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11632219/

有关c++ - 基于参数的 C 预处理器宏特化的更多相关文章

  1. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  2. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  5. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  6. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  7. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  10. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

随机推荐