Hi, is there a way to sometimes pass a type by value, and sometimes by reference? I'm wrapping a C API and would like to emulate a const pointer by using pass-by-reference. (If I use a var argument, it uses a pointer under the hood, but the argument is mutable. The other thing you can do is pass-by-value, which is immutable but doesn't play nice with the API (no pointer).)
It seems, for certain structs, byref is the default anyway (dependent on the struct's size?). I can ensure that a type is passed byref by using the byref pragma:
type MyStruct {.byref.} = object
number: int
Now, in most cases I'd like byref behavior (const pointer to the struct), but it is possible that some functions expect the struct directly on the stack. I don't know it on a type-by-type basis, but only when I come to the individual function. What doesn't work, but would be nice is something like:
proc myfunc(s: MyStruct {.byval.}) = discard
What I also tried was defining another type, and putting the pragma on that:
type MyStruct_ByVal {.byval.} = MyStruct
proc myfunc(s: MyStruct_ByVal) = discard
However, that also didn't work. Any ideas how I can pass the type sometimes byref and sometimes byval? (For clarity, I'm talking about importc`ed functions, but also `cdecl procs that I use as callbacks from C code.)
First of all, I am a bit confused that you use object for a type that is Called MyStruct. Objects have a type header and therefore are not suitable to represent a C-struct, you need a tuple to do that.
Why don't you the var keyword in arguments? If it doesn't work, why dosn't it work for you?
type MyStruct = tuple[number: int]
proc myFuncByVal(s: MyStruct) = discard
proc myFuncByRef(s: var MyStruct) = discard
@Krux02
Objects in Nim don't actually have a type header unless you derive them from RootObj or mark them as {.inheritable.}.. which is the reason you can't derive a type from another which wasn't explicitly marked as a root (via the methods I just mentioned). That way types that don't use inheritance aren't bloated for no reason (making them comparable to C structs).
Additionally, you can mark an object as {.pure inhertibale.} which will allow it to be 'inherited' from without including the type header.. with some limitations of course. Pure inheritable objects loose the ability to use method dynamic dispatch and the ability to do of comparisons "down" the type chain (applicable to ref objects only). Which make them perfect for when tight memory control is a performance concern (eg, an ECS framework, particle system, etc) but you still want use 'inheritance' to help keep code DRY.
Example:
type
Color = tuple[r,g,b:float32]
Vector = tuple[x,y,z:float32]
type
Animal {.inheritable.} = object
position: Vector
Chameleon = object of Animal
color: Color
type
Particle {.pure inheritable.} = object
position: Vector
FancyParticle = object of Particle
color: Color
echo sizeof Animal # 16
echo sizeof Chameleon # 28
echo sizeof Particle # 12
echo sizeof FancyParticle # 24
Example of what you loose with pure inheritance:
proc newChameleon(): ref Chameleon = new result
proc newFancyParticle(): ref FancyParticle = new result
method foobar(a:ref Animal) = echo "Animal"
method foobar(c:ref Chameleon) = echo "Chameleon"
method foobar(p:ref Particle) = echo "Particle" # actually works if only this is defined..
method foobar(f:ref FancyParticle) = echo "FancyParticle" # ERROR!
var c: ref Animal = newChameleon()
var f: ref Particle = newFancyParticle()
if c of Animal: echo 1 # works
if f of Particle: echo 2 # works
if c of Chameleon: echo 3 # works
if f of FancyParticle: echo 4 # ERROR!
foobar(c) # prints 'Chameleon'
foobar(f) # ERROR! (if FancyParticle.foobar is defined)
Also, you can use pure inheritance for zero-overhead nominal type classification, which can be useful for macros. Eg:
type Special {.pure inheritable.} = object
macro register(n:typedesc): typed = # ..process type's members that are of `Special`..
macro register(n:proc): typed = # ..process proc's parameters that are of `Special`..
# ---
type
Foo = object
Bar = object of Special
Thing = object
foo: Foo
bar: Bar
proc update(f:Foo, b:Bar) =
...
register Thing
register update
@filwit
Thanks for the very informative and complete answer. My knowledge when writing my answer was based in the documentation here http://nim-lang.org/docs/manual.html#types-tuples-and-object-types
Objects provide many features that tuples do not. Object provide inheritance and information hiding. Objects have access to their type at runtime, so that the of operator can be used to determine the object's type
This let me assume, that all objects have the overhead. I guess someone should fix that in the documentation.