私はこの問題に対する解決策を見つけましたが、うまくいくようですが、より良い選択肢があるかどうかはわかりません。この解決策は、typed
パラメータにgetTypeImpl
を使用することに基づいています。どのように動作するかを確認するには、単純なタプルとオブジェクトに対してt.getTypeImpl.treeRepr
の出力を見てください。
タプル:
TupleTy
IdentDefs
Sym "x"
Sym "int"
Empty
IdentDefs
Sym "y"
Sym "int"
Empty
IdentDefs
Sym "name"
Sym "string"
Empty
注:::getTypeImpl
のtypeKind
がntyTuple
オブジェクトあるインスタンス(x: 0, y: 1, name: "")
のための型のimplのASTは、次のようになります同じ構造のオブジェクトの型impl ASTは次のようになります。
ObjectTy
Empty
Empty
RecList
IdentDefs
Sym "x"
Sym "int"
Empty
IdentDefs
Sym "y"
Sym "int"
Empty
IdentDefs
Sym "name"
Sym "string"
Empty
注:getTypeImpl
のtypeKind
がntyObject
ある
これは、我々が探している情報がIdentDefs
で利用可能であることを示しています。タプルの場合、IdentDefs
はNimNode
の直接の子ですが、オブジェクトの場合はIdentDefs
がインデックス2の子に格納されます(インデックス0の子にはプラグマ情報が含まれていますが、インデックス1の子は親の情報です)。
マクロように見える可能性が総合(例示のために追加いくつかのデバッグ出力を持つ):
macro iterateFields*(t: typed): untyped =
echo "--------------------------------"
# check type of t
var tTypeImpl = t.getTypeImpl
echo tTypeImpl.len
echo tTypeImpl.kind
echo tTypeImpl.typeKind
echo tTypeImpl.treeRepr
case tTypeImpl.typeKind:
of ntyTuple:
# For a tuple the IdentDefs are top level, no need to descent
discard
of ntyObject:
# For an object we have to descent to the nnkRecList
tTypeImpl = tTypeImpl[2]
else:
error "Not a tuple or object"
# iterate over fields
for child in tTypeImpl.children:
if child.kind == nnkIdentDefs:
let field = child[0] # first child of IdentDef is a Sym corresponding to field name
let ftype = child[1] # second child is type
echo "Iterating field: " & $field & " -> " & $ftype
else:
echo "Unexpected kind: " & child.kind.repr
# Note that this can happen for an object with a case
# fields, which would give a child of type nnkRecCase.
# How to handle them depends on the use case.
# small test
type
TestObj = object
x: int
y: int
name: string
let t = (x: 0, y: 1, name: "")
let o = TestObj(x: 0, y: 1, name: "")
iterateFields(t)
iterateFields(o)