'Nim - How to access mytype of a field that has the type Option[mytype] at compile time?
Heyho,
While coding my way through some generics I stumbled upon an issue with one of my generic functions. I have 2 types such as this
import std/options
import norm
type
A = ref object of Model
name: string
B = ref object of Model
name: string
myA: Option[A]
norm, an ORM in nim for sqlite, has the capability for me to grab the sql-tablename a model belongs to at compile time by just having a type that inherits from Model and calling table()
on it.
For various reasons I want to be able to figure out the name of all tables, that a given Model links to. In this case, B
links to the type A
, but I need to call A.table()
to get that tablename at compile-time (which could be anything if the {.tableName.}
pragma is in use).
However, I can't seem to find a way to access my type, since I can't call the typical get()
method of the options
module at compile time. What is my way out here?
Solution 1:[1]
Thanks to the immensely helpful folks at the nim-discord server (shoutout to leorize there) I was able to solve this issue. In fact, I only made this question so I can google the answer easier myself.
There are multiple ways of going about this:
- Try to access the type directly
The generic parameter of Option
is called T
. Within that T
is the type that you're looking for.
proc getRelatedFieldName[M: Model, O:Model](targetType: typedesc[O], sourceType: typedesc[M]): Option[string] =
let source = sourceType()
for sourceFieldName, sourceFieldValue in source[].fieldPairs:
when sourceFieldValue is Option:
when sourceFieldValue.get() is Model:
when O.table() == sourceFieldValue.T.table():
return some(sourceFieldName)
return none(string)
echo A.getRelatedFieldName(B) # returns "some('myA')"
If you do this with not a typedesc but actual types, you may want to consider using typeof(sourceFieldValue).T.table()
instead.
- Use
typetrait
'sgenericParams
function
You can use the [typetraits][1]
library and its genericParams function.
genericParams(B).get(0)
With genericParams as a tool you can then do interesting things, like iterating over all fields of a type (not an instance!) at compile time, check whether a given field is an Option of a Model and compare tablenames
proc getRelatedFieldName[M: Model, O:Model](targetType: typedesc[O], sourceType: typedesc[M]): Option[string] =
let source = sourceType()
for sourceFieldName, sourceFieldValue in source[].fieldPairs:
when sourceFieldValue is Option:
when sourceFieldValue.get() is Model:
when O.table() == genericParams(sourceFieldValue.type()).get(0).table():
return some(sourceFieldName)
return none(string)
echo A.getRelatedFieldName(B) # returns "some('myA')"
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |