The below code throws an error on compilation. Not that MC_STOP and the other constants are defined like:
const MC_STOP* = (0x00000000) # Timer A mode control: 0 - Stop
const OUTMOD_VAL_4* = (0x00000080) # PWM output mode: 4 - toggle
proc setupTimerISR() =
volatileStore[uint16](TA0CTL, MC_STOP)
volatileStore[uint16](TA0CCTL0, OUTMOD_VAL_4)
volatileStore[uint16](TA0CCR0, 0xFFFF)
volatileStore[uint16](TA0CTL, TASSEL_SMCLK + MC_UP + TACLR)
Error:
blink.nim(8, 24) Error: type mismatch: got <ptr uint16, int32>
but expected one of:
template volatileStore[T](dest: ptr T; val: T)
first type mismatch at position: 2
required type: T
but expression '0x0000FFFF' is of type: int32
expression: volatileStore[uint16](TA0CCR0, 0x0000FFFF)
volatileStore[uint16](TA0CCR0, 0xFFFF)
But then it compiles when I do this:
proc setupTimerISR() =
volatileStore[uint16](TA0CTL, MC_STOP)
volatileStore[uint16](TA0CCTL0, OUTMOD_VAL_4)
volatileStore[uint16](TA0CCR0, 0xFFFF'u16)
volatileStore[uint16](TA0CTL, TASSEL_SMCLK + MC_UP + TACLR)
Why is the 'u16 necessary? It gives the same error if I write it as 0x0000FFFF. Why won't the compiler coerce it to the right type? That will be very tedious.
while it tedious, the compiler expect you to know what you are doing when you mix different size of datatype, or when you use different datatype in an operation.
consider this C code:
int a = 10;
int b = 3;
float c = a / b; /* compiler will allow this but beware that c = 3 and not 3.33333333 */
float d = (float) a / b; /* this time c = 3.3333 while perform hidden conversion on b */
char x = a; /* hidden conversion here */
long y = x; /* hidden conversion here */
unsigned int z = -1; /* compiler allow it but you got very big number */
now in Nim:
var a = 10, b = 3
var c = a.float / b.float # no hidden conversion, you should know what you are doing
# equals to c = `/`(a.float, b.float) because an operator is alias to function call
var x: uint = -1 # compiler disallow it
I found this explicit conversion annoying when I write the code, but at the same time, it force me to rethink the code I write, can I use less conversion an thus make the code run faster?
Hidden conversion could potentially hide bugs and cost performance too.
When reviewing other people's code or maintaining my own code, explicit conversion help me to think more clearly.
btw, you could rewrite your template like this:
template volatileStore[T](dest: ptr T; val: SomeInteger) =
dest[] = T(val) # do the conversion here, and get the convenience when using this template
This is an issue with generics in templates, it's not a problem in procs however.
proc addTo[T](point: ptr T, val: T) =
point[] = point[] + val
var a = 3'u16
addTo[uint16](addr(a), 3)
doAssert(a == 6)
Because parameters with types in templates actually check for expressions with the type, I'd assume the template expected the type of the expression 0xFFFF to be equal to uint16, but it was forced into being int. I guess this could be filed as an issue