v *= 4.0
where v is an N-dimensional vector consisting of scalars of type T. I want to parametrize the `*=` method in terms of T, but nimrod complains about my definition below:
tmp.nim(7, 44) Error: internal error: cannot generate code for: N
I would appreciate an explanation of the error and how to write the code to do what I want here.
type TVec* {.pure, final.}[T; N: static[int]] = object
vec: array[0..N-1, T]
proc vec2*[T](ary: array[0..1, T]): TVec[T,2] {.inline.} =
result.vec = ary
proc `*=`*[T; N: static[int]](v: var TVec[T,N], c: T) {.inline.} =
for i in 0..N-1:
v.vec[i] *= c
when isMainModule:
import unittest
test "vector-scalar arithmetic operators":
var a = vec2([1.0, 2.0])
a *= 4.0
check(a.vec == [4.0, 8.0])
Edits
static[X] is known to have bugs. In particular, when calling *=, the value of N is not properly usable inside the procedure body (it works for the argument types, though). You can work around it by writing:
proc `*=`*[T; N: static[int]](v: var TVec[T,N], c: T) {.inline.} =
for i in 0..len(v.vec)-1:
v.vec[i] *= c
Independently of that issue, you will also need to change the let in the test to var, since *= will modify a.
proc vecn*[T; N: static[int]](ary: array[0..(N-1), T]): TVec[T,N] {.inline.} =
result.vec = ary
...
when isMainModule:
import unittest
test "constructors":
let a = vecn([1.0, 2.0])
to have a general-purpose constructor. I suppose this runs into the same types of bugs? The error I get is
vec.nim(5, 46) Error: ordinal type expected
pointing to the "N" in "array[0..(N-1), T]".
Any insights as to what is currently missing with the handling of static[int]? And what it would take to fix it? Should code written now generally avoid static[int]? I can, of course, do this without the static[int] and manually initialize all instances of the template I need in any program by
2. invoking the template to define the procs for the versions of N that I actually happen to use in my particular program. But that's no fun. :P
The reason is I am playing with some basic types in a fully-generic sense is to gather whether it makes sense to port a particular template-heavy geometric data structure to nim(rod) at this time.
marcianx: Even with your fix, the error remains.
I forgot to mention that you also need to remove the : static[int] constraint from the procedure definition. In short, use static[int] for type declarations, but not for proc declarations.
Still with the caveat that, as the manual says, the static[int] implementation is incomplete. I'm afraid that I haven't dug deeply enough into the compiler to understand what exactly is missing and how stable the existing functionality is.
Thank you both for the information.
Jehan: Thanks for removing static[int] from the proc did the trick for the time being as long as I could avoid using N within the body or the function signature, which is not always possible. What would it take to get something like the following working? (It doesn't work currently, though I can use TVec.T within the function body just fine.) Another thing that worked beautifully was
proc `*=`*(v: var TVec, c: TVec.T) {.inline.} =
for i in 0..len(v.vec)-1:
v.vec[i] *= c
My previous attempts at this had failed because I was still using TVec.N within the body and ran into incomprehensible static[int]-related errors. Unfortunately, I don't know how to write the vecn* function above without referencing N.
Demos: This was less about writing a linear algebra library (these are just static vectors, not dynamic ones) than about testing the macro system to see whether I would be able to port a much more complex C++ geometric library. I guess I will see how far I can get around the limitations for the time being.
proc `*=`*(v: var TVec, c: TVec.T) {.inline.} =
for i in 0..len(v.vec)-1:
`*=`(v.vec[i], c)
But if I try to make it more generic as follows using a template
template defineVectorScalarOperatorAssignment(op: expr) {.immediate.} =
proc op*(v: var TVec, c: TVec.T) {.inline.} =
for i in 0..len(v.vec)-1:
op(v.vec[i], c)
defineVectorScalarOperatorAssignment(`*=`)
Then I get a compiler error
Error: undeclared identifier: 'T'
pointing to the T in "c: TVec.T". Why might this be?