Nim:参数和可变性的地址

时间:2015-05-08 20:46:35

标签: pointers nim

我试图在char背后的Nim政策中下定决心。特别是,我有一个C函数,它接受一些数据缓冲区的指针(+长度等)。我知道此功能将修改数据。简化为:

expression has no address

一方面,这是有道理的,因为参数似乎与type Buffer = object data: seq[float] proc wrapperForCCall(buf: Buffer) = # accessing either buf.addr nor buf.data.addr produces # Error: expression has no address # workaround: var tmp = buf.data # costly copy callToC(tmp.len, tmp.addr) # now it works 绑定完全相同,而且#34;也没有地址"。另一方面,我对手册中的这句话感到困惑:

  

var参数永远不是有效参数传递所必需的。

据我所知,避免复制数据的唯一方法是:

  • 将参数传递为let
  • 传递引用,即使用buf: var Buffer

在这两种情况下,这都表明我的功能会修改数据。此外,它在调用者站点上引入了可变性(即用户不能再使用let绑定它们的缓冲区)。对我来说,关键问题是:因为"我知道" ref object是只读的,我可以说服Nim在没有副本的情况下允许两种不变性吗?我发现这是危险的,因为我必须确定呼叫是不可变的。因此,这将需要某种不安全的地址"机制,允许强制指向不可变数据?

我对参数地址的最后一个谜团:我试图通过将类型更改为callToC来明确复制副本的必要性。在这种情况下,副本已经在呼叫时发生,我希望现在可以访问该地址。为什么在这种情况下拒绝访问?

3 个答案:

答案 0 :(得分:7)

您可以使用shallowCopy来避免buf.data的深层复制,例如:

var tmp: seq[float]
shallowCopy tmp, buf.data

{.byCopy.} pragma只影响调用约定(即对象是在栈上传递还是通过引用传递。

您不能将buf或其任何部分的地址置于refptr之后,因为将值作为非var参数传递的是承诺被调用者不会修改参数。 shallowCopy内置版是一种不安全的功能,可以绕过这种保证(我记得建议shallowCopy应该正确地重命名为unsafeShallowCopy以反映这一点并在其中设置新的shallowCopy第二个参数也是var参数。)

答案 1 :(得分:6)

首先澄清以下内容:

  

var参数永远不是有效参数传递所必需的。

这通常是正确的,因为在Nim中,对象,序列和字符串等复杂值将通过地址(通过引用引用)传递给procs接受只读参数。

当您需要将序列传递给外部C / C ++函数时,事情会变得复杂一些。最常见的方法是依赖openarray类型,它会自动将序列转换为一对数据指针和一个大小整数:

# Let's say we have the following C function:

{.emit: """

#include <stdio.h>

void c_call_with_size(double *data, size_t len)
{
  printf("first value: %f; size: %d \n" , data[0], len);
}

""".}

# We can import it like this:

proc c_call(data: openarray[float]) {.importc: "c_call_with_size", nodecl.}

# The usage is straight-forward:

type Buffer = object
  data: seq[float]

var b = Buffer(data: @[1.0, 2.0])

c_call(b.d)

在生成的C代码中不会有任何副本。

现在,如果包装的C库不接受一对数据/大小参数,如下例所示,我建议在它周围创建一个小的C包装器(你可以创建一个头文件或者只需使用emit pragma创建必要的适配器函数或#defines)。

或者,如果你真的想弄清楚,你可以使用以下帮助程序proc从序列中提取底层缓冲区:

proc rawBuffer[T](s: seq[T]): ptr T =
  {.emit: "result = `s`->data;".}

然后,可以将原始缓冲区传递给C,如下所示:

{.emit: """

#include <stdio.h>

void c_call(double *data)
{
  printf("first value: %f \n", data[0]);
}

""".}

proc c_call(data: ptr float) {.importc: "c_call", nodecl.}

var b = Buffer(data: @[1.0, 2.0])
c_call(b.data.rawBuffer)

答案 2 :(得分:2)

Nim现在有一个unsafeAddr运算符,它允许获取let绑定和参数的地址,从而避免shallowCopy解决方法。显然,必须非常小心,不要改变指针后面的数据。