127 lines
3.9 KiB
Text
127 lines
3.9 KiB
Text
Special Types
|
|
=============
|
|
|
|
static[T]
|
|
---------
|
|
|
|
**Note**: static[T] is still in development.
|
|
|
|
As their name suggests, static parameters must be known at compile-time:
|
|
|
|
.. code-block:: nim
|
|
|
|
proc precompiledRegex(pattern: static[string]): RegEx =
|
|
var res {.global.} = re(pattern)
|
|
return res
|
|
|
|
precompiledRegex("/d+") # Replaces the call with a precompiled
|
|
# regex, stored in a global variable
|
|
|
|
precompiledRegex(paramStr(1)) # Error, command-line options
|
|
# are not known at compile-time
|
|
|
|
|
|
For the purposes of code generation, all static params are treated as
|
|
generic params - the proc will be compiled separately for each unique
|
|
supplied value (or combination of values).
|
|
|
|
Static params can also appear in the signatures of generic types:
|
|
|
|
.. code-block:: nim
|
|
|
|
type
|
|
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
|
|
# Note how `Number` is just a type constraint here, while
|
|
# `static[int]` requires us to supply a compile-time int value
|
|
|
|
AffineTransform2D[T] = Matrix[3, 3, T]
|
|
AffineTransform3D[T] = Matrix[4, 4, T]
|
|
|
|
var m1: AffineTransform3D[float] # OK
|
|
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
|
|
|
|
|
typedesc
|
|
--------
|
|
|
|
`typedesc` is a special type allowing one to treat types as compile-time values
|
|
(i.e. if types are compile-time values and all values have a type, then
|
|
typedesc must be their type).
|
|
|
|
When used as a regular proc param, typedesc acts as a type class. The proc
|
|
will be instantiated for each unique type parameter and one can refer to the
|
|
instantiation type using the param name:
|
|
|
|
.. code-block:: nim
|
|
|
|
proc new(T: typedesc): ref T =
|
|
echo "allocating ", T.name
|
|
new(result)
|
|
|
|
var n = Node.new
|
|
var tree = new(BinaryTree[int])
|
|
|
|
When multiple typedesc params are present, they act like a distinct type class
|
|
(i.e. they will bind freely to different types). To force a bind-once behavior
|
|
one can use a named alias or an explicit `typedesc` generic param:
|
|
|
|
.. code-block:: nim
|
|
proc acceptOnlyTypePairs[T: typedesc, U: typedesc](A, B: T; C, D: U)
|
|
|
|
Once bound, typedesc params can appear in the rest of the proc signature:
|
|
|
|
.. code-block:: nim
|
|
|
|
template declareVariableWithType(T: typedesc, value: T) =
|
|
var x: T = value
|
|
|
|
declareVariableWithType int, 42
|
|
|
|
When used with macros and .compileTime. procs on the other hand, the compiler
|
|
does not need to instantiate the code multiple times, because types then can be
|
|
manipulated using the unified internal symbol representation. In such context
|
|
typedesc acts as any other type. One can create variables, store typedesc
|
|
values inside containers and so on. For example, here is how one can create
|
|
a type-safe wrapper for the unsafe `printf` function from C:
|
|
|
|
.. code-block:: nim
|
|
macro safePrintF(formatString: string{lit}, args: varargs[expr]): expr =
|
|
var i = 0
|
|
for c in formatChars(formatString):
|
|
var expectedType = case c
|
|
of 'c': char
|
|
of 'd', 'i', 'x', 'X': int
|
|
of 'f', 'e', 'E', 'g', 'G': float
|
|
of 's': string
|
|
of 'p': pointer
|
|
else: EOutOfRange
|
|
|
|
var actualType = args[i].getType
|
|
inc i
|
|
|
|
if expectedType == EOutOfRange:
|
|
error c & " is not a valid format character"
|
|
elif expectedType != actualType:
|
|
error "type mismatch for argument ", i, ". expected type: ",
|
|
expectedType.name, ", actual type: ", actualType.name
|
|
|
|
# keep the original callsite, but use cprintf instead
|
|
result = callsite()
|
|
result[0] = newIdentNode(!"cprintf")
|
|
|
|
|
|
Overload resolution can be further influenced by constraining the set of
|
|
types that will match the typedesc param:
|
|
|
|
.. code-block:: nim
|
|
|
|
template maxval(T: typedesc[int]): int = high(int)
|
|
template maxval(T: typedesc[float]): float = Inf
|
|
|
|
var i = int.maxval
|
|
var f = float.maxval
|
|
var s = string.maxval # error, maxval is not implemented for string
|
|
|
|
The constraint can be a concrete type or a type class.
|
|
|
|
|