有没有办法把之前用 %define定义的符号自动取消定义?
我有一个宏,它 %defines一堆变量。我需要它们只在单个函数的持续时间内使用,然后在函数结束时需要把它们 %undefed。这个,比如说,做法并不起作用:
%macro clear_vars 0-*
%rep %0
%undef %1
%rotate 1
%endrep
%endmacro
用法:clear_vars var1, var2, var3, ..., varN 我已经尝试把它们放在引号里,clear_vars "var1",添加前缀,clear_vars _var1, _var2,并尝试把它去掉,以及其他多种变体。
我可以直接在这里放入一堆 %undef var1、%undef varN,或者把源码送进一个预处理器。用NASM的宏是不是就做不到呢?
解决方案
这是NASM的预处理器的一个众所周知的问题。
你不能把一个define名称传给mmacro去操作,若这个define可能已经被定义,因为mmacro的参数展开(%1 及其朋友)不仅会“展开一次”(var1),还会“完全展开”为 var1 被定义成的文本。
我通过对定义名使用众所周知的固定前缀来绕开这个问题。
在我的宏集合中有一个实例,numdef to create a numeric define,前缀是 _。numdef mmacro会检测 _%1 是否已经定义好(通常来自NASM的命令行),并根据这个状态采取不同的动作。这个前缀解决了问题,因为 %ifdef _%1 会把 %1 的文本展开为变量名(不带前缀,且不应当被用作定义本身),然后再在前面追加 _ 前缀,而不会把完整的 _%1 展开成已定义的内容。
在另一个实例中,同样在宏集合里,lframe 系列宏使用 ? 作为前缀。这主要是为了与我们软件中的非lframe变量名区分开来,然而它对你的问题也有同样的好处:如果 %1 本身不是一个定义,你就可以使用类似 ?%1 的名字来检查、定义、重新定义或取消定义变量。我正在这个特性中使用 在 __lleave_undefine_labels mmacro 做与你的用例类似的事情:传递一个smacro名称的列表(不带 ? 前缀),然后在带有 ? 前缀的情况下取消定义或重设/重新定义这些smacros,以在起始的 lframe 之前和 lpar、lvar、或 lequ mmacros的定义之间恢复它们的状态。
如果你的变量名实际上都以 var 开头(不仅仅是举例),那么你可以使用 %undef var%1,这样 var 就是你前缀的使用方式,与我使用 _ 和 ? 的方式相同。
我查看了旧的NASM Bug报告,发现有一个我曾谈及同样问题的案例:
#177不想要隐式mmac参数展开
2010-09-11
附上一个简单的测试用例,展示了后续引入的宏间接运算符 %[...] 如何影响现有代码。请注意,这里对list1和 list4的真正展开(例如在db中,或用于 %warning)最终会导致“无尽的宏递归”错误,这使得对这类列表需要使用宏间接。(涉及 %assign的任何内容仍然可以工作,因为它总是展开。)为了得到预期结果(包含 "0,1,2,3,4" 的列表),你还需要对list3使用宏间接(而不是list2),也就是说把要追加的smac也放上一个宏间接。否则,将追加smac的名称,而不是它的值。
那也没错(除了会破坏现有的宏),但不幸的是mmacro的参数仍然是隐式展开的。我有一些mmacros通常需要对其参数进行非展开处理,因此目前只有一个丑陋的变通办法。因此,我请求:要么mmacro的参数应像现在所有的smacros一样需要显式的宏间接,要么就应该有一种替代方式,在不进行隐式展开的情况下访问宏参数。
我还会附上一个文件,展示为什么我的mmacros需要对参数进行非隐式展开,以及它们在当前隐式展开下的失败原因。我的当前变通方法也会展示出来。
使用预处理器创建列表:展示了为何需要宏间接。
test.asm:
%define list1 0
%define list2 0
%define list3 0
%define list4 0
%macro makelist 1.nolist
%assign var %1
%define list1 list1,var
%define list2 %[list2],var
%define list3 %[list3],%[var]
%define list4 list4,%[var]
%endmacro
makelist 1
makelist 2
makelist 3
makelist 4
%ifdef TW1
%warning List1: list1
%endif
%warning List2: list2
%warning List3: list3
%ifdef TW4
%warning List4: list4
%endif
%ifdef TDB1
db list1
nop
nop
nop
%endif
db list2
nop
nop
nop
db list3
nop
nop
nop
%ifdef TDB4
db list4
nop
nop
nop
%endif
隐式参数展开不允许将参数用作 %define的原样名称。
mmacroparameters.mac:
%macro def 1.nolist
%define %1 38
%endmacro
%macro def2 1.nolist
%define _%1 38
%endmacro
def SMAC ; works as expected (unless SMAC already defined)
def SMAC ; does not work, because the mmacro implicitly fully expands %1
def2 SMAC ; this is the workaround, which works (even multiple times)
def2 SMAC ; but defines the smacro _SMAC, not SMAC
这个最后的示例恰好解决了与你的问题完全相同的难题:对一个可能已经定义的smacro,作为参数传给mmacro进行操作。