fixes #1152
This commit is contained in:
parent
6e53300f83
commit
cb3a38afa2
2 changed files with 48 additions and 32 deletions
|
|
@ -77,38 +77,6 @@ Once bound, typedesc params can appear in the rest of the proc signature:
|
||||||
|
|
||||||
declareVariableWithType int, 42
|
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
|
Overload resolution can be further influenced by constraining the set of
|
||||||
types that will match the typedesc param:
|
types that will match the typedesc param:
|
||||||
|
|
|
||||||
48
tests/macros/typesafeprintf.nim
Normal file
48
tests/macros/typesafeprintf.nim
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
discard """
|
||||||
|
output: '''test 10'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #1152
|
||||||
|
|
||||||
|
import macros, typetraits
|
||||||
|
proc printfImpl(formatstr: cstring) {.importc: "printf", varargs.}
|
||||||
|
|
||||||
|
iterator tokenize(format: string): char =
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
case format[i]
|
||||||
|
of '%':
|
||||||
|
case format[i+1]
|
||||||
|
of '\0': break
|
||||||
|
else: yield format[i+1]
|
||||||
|
i.inc
|
||||||
|
of '\0': break
|
||||||
|
else: discard
|
||||||
|
i.inc
|
||||||
|
|
||||||
|
macro printf(formatString: string{lit}, args: varargs[typed]): untyped =
|
||||||
|
var i = 0
|
||||||
|
let err = getType(bindSym"ValueError")
|
||||||
|
for c in tokenize(formatString.strVal):
|
||||||
|
var expectedType = case c
|
||||||
|
of 'c': getType(bindSym"char")
|
||||||
|
of 'd', 'i', 'x', 'X': getType(bindSym"int")
|
||||||
|
of 'f', 'e', 'E', 'g', 'G': getType(bindSym"float")
|
||||||
|
of 's': getType(bindSym"string")
|
||||||
|
of 'p': getType(bindSym"pointer")
|
||||||
|
else: err
|
||||||
|
|
||||||
|
var actualType = getType(args[i])
|
||||||
|
inc i
|
||||||
|
|
||||||
|
if sameType(expectedType, err):
|
||||||
|
error c & " is not a valid format character"
|
||||||
|
elif not sameType(expectedType, actualType):
|
||||||
|
error "type mismatch for argument " & $i & ". expected type: " &
|
||||||
|
$expectedType & ", actual type: " & $actualType
|
||||||
|
|
||||||
|
# keep the original callsite, but use cprintf instead
|
||||||
|
result = callsite()
|
||||||
|
result[0] = bindSym"printfImpl"
|
||||||
|
|
||||||
|
printf("test %d\n", 10)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue