Skip to content

Commit 4cd86c0

Browse files
timotheecourAraq
authored andcommitted
typetraits: fixes #6454; genericParams; added lenTuple; added tuple type get (#13064)
1 parent fcd2f30 commit 4cd86c0

File tree

4 files changed

+60
-4
lines changed

4 files changed

+60
-4
lines changed

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
- Added `sugar.capture` for capturing some local loop variables when creating a closure.
4848
This is an enhanced version of `closureScope`.
4949

50+
- Added `typetraits.lenTuple` to get number of elements of a tuple/type tuple,
51+
and `typetraits.get` to get the ith element of a type tuple.
52+
- Added `typetraits.genericParams` to return a tuple of generic params from a generic instantiation
53+
5054
## Library changes
5155

5256
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`

lib/pure/typetraits.nim

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
export system.`$` # for backward compatibility
1616

17+
include "system/inclrtl"
1718

1819
proc name*(t: typedesc): string {.magic: "TypeTrait".}
1920
## Returns the name of the given type.
@@ -70,6 +71,45 @@ proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".}
7071
## Returns base type for distinct types, works only for distinct types.
7172
## compile time error otherwise
7273

74+
import std/macros
75+
76+
macro lenTuple*(t: tuple): int {.since: (1, 1).} =
77+
## Return number of elements of `t`
78+
newLit t.len
79+
80+
macro lenTuple*(t: typedesc[tuple]): int {.since: (1, 1).} =
81+
## Return number of elements of `T`
82+
newLit t.len
83+
84+
when (NimMajor, NimMinor) >= (1, 1):
85+
template get*(T: typedesc[tuple], i: static int): untyped =
86+
## Return `i`th element of `T`
87+
# Note: `[]` currently gives: `Error: no generic parameters allowed for ...`
88+
type(default(T)[i])
89+
90+
macro genericParams*(T: typedesc): untyped {.since: (1, 1).} =
91+
## return tuple of generic params for generic `T`
92+
runnableExamples:
93+
type Foo[T1, T2]=object
94+
doAssert genericParams(Foo[float, string]) is (float, string)
95+
result = newNimNode(nnkTupleConstr)
96+
var impl = getTypeImpl(T)
97+
expectKind(impl, nnkBracketExpr)
98+
impl = impl[1]
99+
while true:
100+
case impl.kind
101+
of nnkSym:
102+
impl = impl.getImpl
103+
continue
104+
of nnkTypeDef:
105+
impl = impl[2]
106+
continue
107+
of nnkBracketExpr:
108+
for i in 1..<impl.len:
109+
result.add impl[i]
110+
break
111+
else:
112+
error "wrong kind: " & $impl.kind
73113

74114
when isMainModule:
75115
static:

lib/system/inclrtl.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,7 @@ else:
5050
{.pragma: benign, gcsafe.}
5151

5252
template since(version, body: untyped) {.dirty.} =
53+
## limitation: can't be used to annotate a template (eg typetraits.get), would
54+
## error: cannot attach a custom pragma.
5355
when version <= (NimMajor, NimMinor):
5456
body

tests/metatype/ttypetraits.nim

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ block: # typeToString
4444
doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 ==
4545
"tuple[a: C2b{C}[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]"
4646

47-
48-
#----------------------------------------------------
49-
5047
block distinctBase:
5148
block:
5249
type
@@ -90,4 +87,17 @@ block distinctBase:
9087
doAssert($distinctBase(typeof(b2)) == "string")
9188
doAssert($distinctBase(typeof(c2)) == "int")
9289

93-
90+
block genericParams:
91+
type Foo[T1, T2]=object
92+
doAssert genericParams(Foo[float, string]) is (float, string)
93+
type Foo1 = Foo[float, int]
94+
doAssert genericParams(Foo1) is (float, int)
95+
type Foo2 = Foo[float, Foo1]
96+
doAssert genericParams(Foo2) is (float, Foo[float, int])
97+
doAssert genericParams(Foo2) is (float, Foo1)
98+
doAssert genericParams(Foo2).get(1) is Foo1
99+
doAssert (int,).get(0) is int
100+
doAssert (int, float).get(1) is float
101+
static: doAssert (int, float).lenTuple == 2
102+
static: doAssert (1, ).lenTuple == 1
103+
static: doAssert ().lenTuple == 0

0 commit comments

Comments
 (0)