2016-04-22 96 views
1

#运算符的用途是什么,称为stringizer运算符, 以及它如何使用?stringizer运算符#

我的书以下面的方式描述#,光头部分是我不明白的。

的stringizer操作者#必须跟一个参数和它们被替换为从参数的令牌,这是不先被替换即,以下输入池莉构建文字的字符串:

#define W 124 
#define str(a) (#a) 

str(W) 

产生输出:

“W”

如果我们想有一个字符串字面与宏定义,我们必须使用两个函数宏:

#define W 124 
#define xstr(a) (#a) 
#define str(b) (xstr(b)) 

产生输出

“124”

这样做的原因是,以前的参数是参数b的参数被完全取代替换为str的替换列表,这意味着xstr的调用将使用124作为参数,然后在xstr中将其字符串化为参数

+0

想想这个问题,如果宏观膨胀是渴望或懒惰。为了帮助您,请阅读关于haskell的懒惰论证评估与几乎任何其他语言的渴望论证评估。 – BitTickler

回答

5

因此要理解这一点,预处理器会处理这些事情。在第一个例子我们可以看到如下str(W) -> #W -> "W"

#define W 124 
#define str(a) (#a) 

str(W) 

会得到处理。

现在,如果我们把第二个例子

#define W 124 
#define xstr(a) (#a) 
#define str(b) (xstr(b)) 

str(W) 

会做如下处理:str(W) -> xstr(124) -> #124最后"124"

2

#A强制编译器把a的值,这在这种情况下是一个宏参数,用双引号。这个字符串的引用在宏内的替换之前发生。因此你得到了“W”。

您可以通过将您的源代码传递给C预处理器来看到这一点;你不需要在这里有一个有效的C程序。在Linux主机上,而忽略了“#”和空行绒毛由CPP生产,我们得到:

 cpp macro-strings.c 
    ("W")

添加间接的级别允许扩展W¯¯值。由于外部宏中没有“#”,所以在内部宏扩展之前,该宏内的替换发生,这将创建字符串。

如果您注释掉XSTR定义,它可能会变得更加直观:

#define W 124 
/* #define xstr(a) (#a) */ 
#define str(b) (xstr(b)) 

cpp macro-strings.c 
(xstr(124))

删除注释产生适当的(( “124”))

而且千万记住,相邻的双引用的字符串由编译器合并为一个字符串:

"Hello," " " "world!" is the same as "Hello, world!"

当一个或多个字符串s是在宏内生成的。

-2

如果时间不是问题,理解常常来自“自己动手”的方法。在这里,如果问题是“预处理器在做什么?”,为什么不写一个简化的玩具预处理器并研究结果。我忘了名字,但有一个“法律”,说:“你只有真正明白,如果你能实现它”。

当然,编写一个完整的C/C++预处理器并不是很有吸引力。因此,下面的代码试图获得一个足够大,但极简主义的简化模型实现,它只是能够展示基本上正在发生的事情。一种紧凑的语言已被选中,以保持行数低。如果您使用抽象语法树级别而不是使用语法,则不需要解析器。

type MacroExpr = 
    | MacroDef of string * string list * MacroExpr 
    | MacroCall of string * (string * MacroExpr) list 
    | String of string 
    | Stringize of MacroExpr 
    | ArgRef of string 

let rec run (macros : MacroExpr list) (call : MacroExpr) : MacroExpr = 
    let value_of arg_name = 
     match call with 
     | MacroCall(n,args) -> snd (List.find (fun a -> fst a = arg_name) args) 
     | _ -> failwith "bla bla" 
    match call with 
    | MacroCall(n,al) -> 
     let m = macros |> List.find (fun m -> match m with | MacroDef(n1,_,_) -> n1 = n | _ -> false) 
     let mname,margs,mbody = 
      match m with 
      | MacroDef(a,b,c) -> a,b,c 
      | _ -> failwithf "macros argument contains something else: %A" m 
     let rec x_to_string = function 
      | String s -> s 
      | Stringize x -> x_to_string x 
      | ArgRef(an) -> x_to_string (eval (snd(al |> List.find (fun (n,_) -> n = an)))) 
      | MacroDef(_,_,_) -> failwith "No nested macros supported." 
      | MacroCall(n,_) -> n 
     and eval = function 
      | String s -> String s 
      | Stringize x -> String (x_to_string x) 
      | ArgRef(an) -> String (x_to_string (snd(al |> List.find (fun (n,_) -> n = an)))) 
      | MacroDef(_,_,_) -> failwith "No nested macros supported." 
      | MacroCall(n,al1) -> 
       run macros (MacroCall(n, al1 |> List.map (fun (an,ax) -> an,eval ax))) 

     match mbody with 
     | ArgRef(an) -> snd(al |> List.find (fun (n,_) -> n = an)) 
     | Stringize(x) -> String(x_to_string x) 
     | String s -> String s 
     | MacroCall(mn,al1) -> 
      run macros (MacroCall(mn, al1 |> List.map (fun (an,ax) -> an,eval ax))) 
     | MacroDef(_,_,_) -> failwithf "No nested macros supported. Found one in %s: %A" n mbody 
    | _ -> failwithf "'call' argument shall be a MacroCall expression but is a %A" call 


let macros = 
    [ 
     MacroDef ("W", [], String "124") 
     MacroDef ("str1", ["a"], Stringize (ArgRef "a")) 
     MacroDef ("str2", ["a"], MacroCall("str1", ["a",ArgRef "a"])) 
    ] 

let calls = 
    [ 
     MacroCall("W",[]) 
     MacroCall("str1",["a",MacroCall("W",[])]) 
     MacroCall("str2",["a",MacroCall("W",[])]) 
    ] 


calls 
|> List.map (fun c -> run macros c) 

macros变量包含我们想要探索的预定义宏的列表。它们被定义为(最小,低科技)​​类型。

calls变量包含一个宏调用列表,使用相同的技术。

即使我们忽略了功能run的目前的执行情况,我们可以看到一些有趣的事情:

为“str2的”宏定义转发其参数“a”到宏调用STR1。 ArgRef - 对“str2”参数列表的引用。遵循急切评估原则的语言不会将该ArgRef传递给下一个宏评估,而是尝试首先解决它。毕竟,对“str1”的评估必须知道调用者“str2”的arglist。

因此,从str2调用str1的参数是["a",MacroCall("W",[])],而首先通过评估宏“W”到String "124"来直接调用宏str1和["a",MacroCall("W",[])]

的最后两行中运行的所有呼叫的代码和对结果产生如预期不:

VAL它:MacroExpr列表= [字符串“124”;字符串“124”;字符串“W”]

现在,作为一个练习,您可以尝试更改运行函数,使其像c预处理器那样工作。提示:子功能x_to_stringeval是罪犯)

的那些2子功能固定的版本是:

let rec x_to_string = function 
     | String s -> s 
     | Stringize x -> x_to_string x 
     | ArgRef(an) -> x_to_string ((*eval*) (snd(al |> List.find (fun (n,_) -> n = an)))) // disabled eval 
     | MacroDef(_,_,_) -> failwith "No nested macros supported." 
     | MacroCall(n,_) -> n 
    and eval = function 
     | String s -> String s 
     | Stringize x -> String (x_to_string x) 
     | ArgRef(an) -> String (x_to_string (eval (snd(al |> List.find (fun (n,_) -> n = an))))) // added eval 
     | MacroDef(_,_,_) -> failwith "No nested macros supported." 
     | MacroCall(n,al1) -> 
      run macros (MacroCall(n, al1 |> List.map (fun (an,ax) -> an,eval ax))) 

...现在结果如预期:

确定它:宏EXPR list = [String“124”;字符串“W”;字符串“124”]