Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm trying to make up my mind about Nim's policy behind expression has no address. In particular, I have a C function which takes a pointer (+ length etc.) of some data buffer. I know that this function will not modify the data. Simplified:

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

On the one hand this makes sense, since a parameter seems to behave exactly like a let binding, which also "has no address". On the other hand, I'm puzzled by this statement in the manual:

var parameters are never necessary for efficient parameter passing.

As far as I can see, the only way to avoid copying the data is by either:

  • passing the parameter as buf: var Buffer
  • passing a reference, i.e., using a ref object.

In both cases this suggests that my function modifies the data. Furthermore, it introduces mutability on the caller site (i.e. users can no longer use let bindings for their buffers). The key question for me is: Since "I know" that callToC is read-only, can I convince Nim to allow both immutability without a copy? I see that this is dangerous, since I have to know for sure that the call is immutable. Thus, this would require some sort of "unsafe address" mechanism, allowing to force pointers to immutable data?

And my final mystery of parameter addresses: I tried to make the necessity of the copy explicit by changing the type to Buffer {.bycopy.} = object. In this case the copy already happens at call time, and I would expect to have access to the address now. Why is the access denied in this case as well?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
769 views
Welcome To Ask or Share your Answers For Others

1 Answer

You can avoid the deep copy of buf.data by using shallowCopy, e.g.:

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

The {.byCopy.} pragma only affects the calling convention (i.e. whether an object gets passed on the stack or via a reference.

You cannot take the address of buf or any part of it that isn't behind a ref or ptr because passing a value as a non-var parameter is a promise that the callee does not modify the argument. The shallowCopy builtin is an unsafe feature that circumvents that guarantee (I remember suggesting that shallowCopy should properly be renamed to unsafeShallowCopy to reflect that and to have a new shallowCopy where the second argument is a var parameter also).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...