I started learning Nim recently. Is there a way to convert a tuple into a object? What I want is to do the following.
proc createRef(typeinfo:typedesc, data:tuple):auto =
result = new(typeinfo)
#fill the result object with the values in the tuple ???
An unsafe but fast and clean way - just to cast your tuple to your object type:
proc createRef(typeinfo: typedesc, data: tuple): auto =
result = new(typeinfo)
result[]=cast[typeinfo](data)
Be aware you should provide a tuple of values of needed types, or the same in size, and not just compatible:
type
O = object
x: int
y: float
z: int8
q: int8
let r = createRef(O, (5, 6.6, 7i8, 8i8))
echo r[]
In this example, if you provide the 3rd as int, not expected int8, you won't get the 4th item of tuple into your object - 7 literal will pass an int value (32/64 bits), setting z and q fields to 7 and 0 respectively.
@LeGuim I guess bits would be truncated if you do the unsafe cast.
I created the following macros. It's a bit too verbose though - considering the python/c++.
macro settattr(attr:static[string]): typed =
newNimNode(nnkAsgn).add(
newNimNode(nnkDotExpr).add(
newIdentNode(!"obj"),
newIdentNode(attr)
),
newNimNode(nnkDotExpr).add(
newIdentNode(!"data"),
newIdentNode(attr)
)
)
proc fillObj[T](obj:ref T,data:object|tuple) =
for k,v in data.fieldPairs:
settattr(k)
proc createRef(T:typedesc, data:tuple|object): auto =
result = new(T)
fillObj(result,data)
proc createRef(data:object): auto =
result = new(type(data)
fillObj(result, data)
Why they should be truncated, if they are of the same size?
And your macro assigns fields by names... That is, it in the end doesn't allow you to write like createRef(O, (5, 6.6, 7i8, 8i8)), just providing a type and values.
My macro:
import macros
macro createObj(typ: typedesc, data: untyped): untyped =
# This macro only accepts tuples in parenthesis, but this can be changed
assert data.kind == nnkPar
# Object construction syntax
result = newTree(nnkObjConstr)
# Add type name
result.add(typ.getTypeInst()[1])
# Get implementation of typedesc
let typImpl = typ.getTypeInst()[1].symbol.getImpl()[2]
# We support both ref and usual objects
let fields = if typImpl.kind == nnkRefTy: typImpl[0][2] else: typImpl[2]
# Tuple should have exactly the same number of values as object
assert fields.len == data.len
# add fieldName: value to result for all fields/values
for i in 0..<fields.len:
result.add newTree(nnkExprColonExpr, fields[i][0], data[i])
# Result would be something like MyObj(x: 5, y: 7.0)
type MyObj = ref object
x: int
y: float
let myObj = createObj(MyObj, (5, 7.0))
echo myObj.y
My intuition tells me that you did something wrong in your types, if you want to convert an object to a tuple or vice versa. Try to redesign your code so that you either only use tuples or only use objects, but don't try to make mixing them up easy.
If you still want to make it easy to mix up tuple/objects yea macros will help you implement this.
Get off my lawn, dude. Passing a tuple to a factory proc is definitely a neat, and clean, thing to do.
You can construct a tuple and pass it in, and use that to construct an object. Much neater than a huge parameter list, I think.
Sometimes.
It depends. As always.
Are you sure you want to lecture me about coding practices, considering that you don't know me and I don't know you?
I am talking about constructing an object using a tuple, not casting a tuple to an object.
My use case would be to pass a tuple to a factory function and have it construct an object using the elements of the tuple.
Same use case as you would pass a struct to a create_object function in C, I guess. Only, with a macro and a tuple, the code would be much neater.
@LeuGim Yeah if it has the same fields there is no problem. But we can't be sure that is always the case.
@jacmoe Thanks. I got tired of creating reference using the PIMPL or the following pattern
var s:ref Person
new(s)
s.age ...
so I finally got
var s = createRef(Person,(age:..))
# or
var s = createRef(Person(age:...))
Is there a way to create a macro for := for the same thing?
var s := Person(age:...)
Replace createRef with:
proc `:=`(v: var ref object, data:tuple|object) =
v = new(v.type)
fillObj(v,data)
Use like this:
var r: ref O # O - some object type
r := (x: 5, y: 6.6, z: 7i8, q: 8i8)
echo r[]
@zuko95, CMIIW, you can do it like this
import typeinfo
type
Person = ref object
age: int
name: string
var people = newSeq[Person]()
proc `$`(p: Person): string =
p.name & " is " & $p.age & " years old"
proc refFromAnotherScope() =
people.add Person(name: "scoped general", age: 30)
proc main =
var s = Person(name: "general", age: 50)
echo s
case s.toAny.kind
of akRef:
echo "s is reference type"
else:
echo "s is something else"
echo()
refFromAnotherScope() # define Person from another function
echo people[0]
case people[0].toAny.kind
of akRef:
echo people[0].name, " is reference type"
else:
echo people[0], " is something else"
main()
I got tired of creating reference using the PIMPL or the following pattern
var s:ref Person
new(s)
s.age ...
Er, I think you use Nim wrong, (ref) object construction can be done like this:
var s = (ref Person)(age: 3, name: "Burns")
No tuples, no copying, no macros involved.
@Araq What Nim version? It fails with "object constructor needs an object type" on my fresh-downloaded Nim 1.17.0...
By the way, it is counter-intuitive, I guess. It wouldn't be so if the syntax was available for any type, e.g. arrays. It's a bit misleading as casts can look a bit similar to constructors:
type
Modular[N: static[Positive]] = distinct range[0..N]
ModularSet[N: static[Positive]] = distinct array[N, bool]
let mr1 = Modular[3](2) # cast
let ms1 = ModularSet[3]([true, false, true]) #cast
let mr2 = Modular[17]() # constructor, error: object constructor needs an object type
let ms2 = ModularSet[17]() # constructor, error: object constructor needs an object type
Please note the warnings about initialization (can't Nim prove 3 or 17 are Positive?).
Here, of course, one can just add type declarations without initialization but...
let mr1 = Modular[4](2)
let mr2 = Modular[4](3)
let ms = ModularSet[4]([true, false, false, true])
proc contains[N: static[Positive]]
(set: ModularSet[N], num: Modular[N]): bool = ...
echo (mr1 in ms) # false
echo (mr2 in ms) # true
echo (mr1 in ModularSet[7]()) # fails
Firstly: it fails for N: static[Positive], we need to use N: static[int]. Once again: can't Nim infer 4 is Positive?
Secondly: the last one would actually work for an object.