模板和宏中的类型化 vs 非类型化 vs expr vs stmt [英] typed vs untyped vs expr vs stmt in templates and macros

查看:13
本文介绍了模板和宏中的类型化 vs 非类型化 vs expr vs stmt的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在使用模板和宏,但我不得不说我几乎没有找到关于这些重要类型的信息.这是我肤浅的理解:

I've been lately using templates and macros, but i have to say i have barely found information about these important types. This is my superficial understanding:

  • typed/expr 是之前必须存在的东西,但您可以使用 .immediate.克服它们.
  • untyped/stmt 是之前没有定义的东西/一个或多个语句.

这是一个非常模糊的类型概念.我想对它们有更好的解释,包括哪些类型应该用作返回.

This is a very vague notion of the types. I'd like to have a better explanation of them, including which types should be used as return.

推荐答案

这些不同参数类型的目标是在指定编译器应该接受什么作为宏的参数时为您提供几个不断提高的精度.

The goal of these different parameter types is to give you several increasing levels of precision in specifying what the compiler should accept as a parameter to the macro.

让我们想象一个可以求解数学方程的假设宏.它将像这样使用:

Let's imagine a hypothetical macro that can solve mathematical equations. It will be used like this:

solve(x + 10 = 25) # figures out that the correct value for x is 15

这里,宏只关心提供的 AST 树的结构.它不要求同一棵树是当前范围内的有效表达式(即定义 x 等等).该宏只是利用了 Nim 解析器,该解析器已经可以解码大多数数学方程,将它们变成更容易处理的 AST 树.这就是 untyped 参数的用途.它们没有经过语义检查,您会得到原始 AST.

Here, the macro just cares about the structure of the supplied AST tree. It doesn't require that the same tree is a valid expression in the current scope (i.e. that x is defined and so on). The macro just takes advantage of the Nim parser that already can decode most of the mathematical equations to turn them into easier to handle AST trees. That's what untyped parameters are for. They don't get semantically checked and you get the raw AST.

精度阶梯的下一步是 typed 参数.它们允许我们编写可以接受任何表达式的通用宏,只要它在当前范围内具有适当的含义(即可以确定其类型).除了更早地捕获错误之外,这还有一个好处是我们现在可以使用宏体内的表达式类型(使用 macros.getType 过程).

On the next step in the precision ladder are the typed parameters. They allow us to write a generic kind of macro that will accept any expression, as long as it has a proper meaning in the current scope (i.e. its type can be determined). Besides catching errors earlier, this also has the advantage that we can now work with the type of the expression within the macro body (using the macros.getType proc).

我们可以通过要求特定类型(具体类型或类型类/概念)的表达式来获得更精确的信息.宏现在将能够像常规 proc 一样参与重载决议.重要的是要理解宏仍然会接收 AST 树,因为它会接受可以在编译时计算的表达式和只能在运行时计算的表达式.

We can get even more precise by requiring an expression of a specific type (either a concrete type or a type class/concept). The macro will now be able to participate in overload resolution like a regular proc. It's important to understand that the macro will still receive an AST tree, as it will accept both expressions that can be evaluated at compile-time and expressions that can only be evaluated at run-time.

最后,我们可以要求宏接收在编译时提供的特定类型的值.宏可以使用此值来参数化代码生成.这是静态参数的领域.在宏的主体内,它们不再是 AST 树,而是普通的类型良好的值.

Finally, we can require that the macro receives a value of specific type that is supplied at compile-time. The macro can work with this value to parametrise the code generation. This is realm of the static parameters. Within the body of the macro, they are no longer AST trees, but rather ordinary well typed values.

到目前为止,我们只讨论了表达式,但 Nim 的宏也接受和生成块,这是我们可以控制的第二个轴.expr 一般表示单个表达式,而 stmt 表示表达式列表(历史上,它的名字来源于StatementList,在表达式和语句统一之前,它作为一个单独的概念存在尼姆).

So far, we've only talked about expressions, but Nim's macros also accept and produce blocks and this is the second axis, which we can control. expr generally means a single expression, while stmt denotes a list of expressions (historically, its name comes from StatementList, which existed as a separate concept before expressions and statements were unified in Nim).

用模板的返回类型最容易说明区别.考虑系统模块中的 newException 模板:

The distinction is most easily illustrated with the return types of templates. Consider the newException template from the system module:

template newException*(exceptn: typedesc, message: string): expr =
  ## creates an exception object of type ``exceptn`` and sets its ``msg`` field
  ## to `message`. Returns the new exception object.
  var
    e: ref exceptn
  new(e)
  e.msg = message
  e

即使认为构造异常需要几个步骤,通过指定 expr 作为模板的返回类型,我们告诉编译器只有最后一个表达式将被视为返回值模板.其余语句将被内联,但巧妙地对调用代码隐藏.

Even thought it takes several steps to construct an exception, by specifying expr as the return type of the template, we tell the compiler that only that last expression will be considered as the return value of the template. The rest of the statements will be inlined, but cleverly hidden from the calling code.

作为另一个例子,让我们定义一个特殊的赋值运算符,它可以模拟 C/C++ 的语义,允许在 if 语句中进行赋值:

As another example, let's define a special assignment operator that can emulate the semantics of C/C++, allowing assignments within if statements:

template `:=` (a: untyped, b: typed): bool =
  var a = b
  a != nil

if f := open("foo"):
  ...

指定具体类型与使用 expr 具有相同的语义.如果我们使用默认的 stmt 返回类型,编译器将不允许我们传递表达式列表",因为 if 语句显然需要一个表达式.

Specifying a concrete type has the same semantics as using expr. If we had used the default stmt return type instead, the compiler wouldn't have allowed us to pass a "list of expressions", because the if statement obviously expects a single expression.

.immediate. 是很久以前的遗留物,当时模板和宏不参与重载决议.当我们第一次让他们知道类型系统时,很多代码需要当前的 untyped 参数,但是重构编译器从一开始就引入它们太难了,而是我们添加了 .immediate. pragma 作为强制整个宏/模板向后兼容行为的一种方式.

.immediate. is a legacy from a long-gone past, when templates and macros didn't participate in overload resolution. When we first made them aware of the type system, plenty of code needed the current untyped parameters, but it was too hard to refactor the compiler to introduce them from the start and instead we added the .immediate. pragma as a way to force the backward-compatible behaviour for the whole macro/template.

使用 typed/untyped,您可以更精细地控制宏的各个参数,并且 .immediate. pragma 将逐渐被淘汰和弃用.

With typed/untyped, you have a more granular control over the individual parameters of the macro and the .immediate. pragma will be gradually phased out and deprecated.

这篇关于模板和宏中的类型化 vs 非类型化 vs expr vs stmt的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆