10

の背後にある Nim のポリシーについて決心しようとしていexpression has no addressます。特に、データ バッファーのポインター (+ 長さなど) を受け取る C 関数があります。この関数がデータを変更しないことはわかっています。簡略化:

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

一方では、これは理にかなっています。パラメーターは、let「アドレスを持たない」バインドとまったく同じように動作するように見えるからです。一方、私はマニュアルの次の記述に困惑しています。

var パラメーターは、効率的なパラメーターの受け渡しには必要ありません。

私の知る限り、データのコピーを回避する唯一の方法は次のいずれかです。

  • パラメータを次のように渡すbuf: var Buffer
  • 参照を渡す、つまりref object.

どちらの場合も、これは私の関数がデータを変更することを示唆しています。さらに、呼び出し元サイトに可変性が導入されます (つまり、ユーザーはバッファーに let バインディングを使用できなくなります)。私にとっての重要な質問は次のとおりです。「私は知っている」callToCが読み取り専用であるため、コピーなしで両方の不変性を許可するように Nim を説得できますか? 呼び出しが不変であることを確実に知る必要があるため、これは危険です。したがって、これにはある種の「安全でないアドレス」メカニズムが必要になり、不変データへのポインタを強制できますか?

そして、パラメーター アドレスの最後の謎: タイプを に変更して、コピーの必要性を明示しようとしましたBuffer {.bycopy.} = object。この場合、コピーは呼び出し時に既に行われており、今すぐアドレスにアクセスできると予想されます。この場合もアクセスが拒否されるのはなぜですか?

4

3 に答える 3

7

buf.dataたとえば、以下のように、shallowCopy を使用してディープ コピーを回避できます。

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

{.byCopy.}プラグマは、呼び出し規則 (つまり、オブジェクトがスタック上で渡されるか、参照を介して渡されるか) にのみ影響します。

orの背後にないアドレスbufまたはその一部を取得することはできません。値を非 var パラメーターとして渡すことは、呼び出し先が引数を変更しないという約束であるためです。ビルトインは、その保証を回避する危険な機能です (それを反映して、2 番目の引数もパラメーターである新しいものを作成するために、適切に名前を変更する必要があることを提案したことを覚えています)。refptrshallowCopyshallowCopyunsafeShallowCopyshallowCopyvar

于 2015-05-08T21:24:33.940 に答える
6

次のことを明確にすることから始めましょう。

var パラメーターは、効率的なパラメーターの受け渡しには必要ありません。

Nim では、オブジェクト、シーケンス、文字列などの複雑な値は、読み取り専用パラメーターを受け入れる proc にアドレス (別名参照) で渡されるため、これは一般的に当てはまります。

シーケンスを外部 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 ライブラリが data/size 引数のペアを受け入れない場合は、その周りに小さな C ラッパーを作成することをお勧めします (ヘッダー ファイルを作成するか、または単に emit プラグマを使用して作成することができます)。必要なアダプター関数または #defines)。

または、本当に手を汚したい場合は、次のヘルパー プロシージャを使用して、シーケンスから基になるバッファーを抽出できます。

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)
于 2015-05-10T09:36:02.400 に答える