Take the following example. Obviously the doSomething function is wrong but should hint what I want to do.
type
TestObj = object
text: string
value1: int
value2: int
type
TestGeneric[T, MEMBER] = object
internalVal: T
proc doSomething[T, MEMBER](obj: TestGeneric[T, MEMBER], newVal: int) =
...
let offset = offsetOf(T, MEMBER)
...
var x = T(text: "test", value1: 1, value2: 5)
x.MEMBER = newVal
internalVal = x
...
I want to do operations with a compile time selected member variable in a generic type. Generics don't seem to accept untyped parameters so I have so far understood that you have two ways to point out the member variable either using a string or type.member like in the example below.
let x = TestGeneric[TestObj , TestObj.value1]()
... alternatively ...
let x = TestGeneric[TestObj , "value1"]()
offsetOf takes a type or a value and then an identifier. Neither TestObj.value1 or a "value1" will satisfy offsetOf second argument as it only wants an untyped identifier. Ironically offsetOf seems to trickle down to an intrinsic offsetOfDotExpr(value.member) which almost seems to use type.member syntax (https://github.com/nim-lang/Nim/blob/version-1-6/lib/system.nim#L635) . Maybe a type.member version of it could be useful. Regardless, offsetOf needs an identifier which neither TestObj.value1 or "value1" are so the question is how I can convert it to one.
For using x.MEMBER in the example, it seems like there are dereference operators for the any object (https://nim-lang.org/docs/typeinfo.html#%5B%5D%3D%2CAny%2CAny). However, these require some kind of conversion to an any object and using the string to identify the member variable must be used. I have not been able to get these to work. The question is also if there is a performance penalty when using these. Also another question is if there are more direct ways to achieve this.
With a simple macro you can work with the string "value1" and convert it to an identifier node to be used in offsetOf.
type
TestObj = object
text: string
value1: int
value2: int
type
TestGeneric[T; MEMBER: static string] = object
internalVal: T
import macros
macro offsetOfStrField(T: untyped; field: static string): untyped =
result = newCall(bindSym"offsetOf", T, ident(field))
proc doSomething[T; MEMBER: static string](obj: TestGeneric[T, MEMBER]) =
echo offsetOfStrField(T, MEMBER)
let x = TestGeneric[TestObj, "value1"]()
doSomething(x) # 8