Hi - I am new to nim and it would be great of one of you could point me in the right direction.
I have many (>100) functions from a c library taking different combinations of parameter (int32, int64, float32, float64 and so on).
And I would like to come up with something like
prod add*[L,R,T](lhs: L, rhs: R) : T
(which does not work because T is not defined)
L and R could be every combination of int32, uint32, int64, uint64, float32, float64 and so on and writing all combinations be hand seems very impractical
T depends on L and R (e.g int32, int64 -> int64) and I can "calculate" T based on L and R at compile time if I could call a proc to generate the target type T - if you could point my to some concepts I could us or an example solving a similar case this would be fantastic.
Many thanks in advance!
I also just started with Nim two days ago, so maybe I'm not the most qualified person to answer ;)
I think you need a template here:
template add*[L,R](lhs: L, rhs: R): auto =
lhs + rhs
The compiler should then happily create the procs for all those combinations being usedMany thanks for your idea! Unfortunately this results in "has no type or is ambiguous" - and I am not sure how the compiler should get the information for automatic type inference, but I have to admit that my understanding of the mechanisms is still very limited.
The example may not have been a good one because L,R and T are generic container types which may also be a source of complication.
Kind Regards
Have you tried
proc f(L, R: typedesc): typedesc =
when L is int32 and R is int64: int32
...
proc add*[L,R](lhs: L, rhs: R) : f(L, R)
Have not tried it, but I think it should work
@bitstorm: well in case of simple types like int64, int32, float, ... the compiler is clever enough to get the correct type. For example (tested code):
import typetraits
template myadd[L,R](lhs: L, rhs: R): auto =
lhs + rhs
var intAndFloat = myadd(5, 5.0)
# echos "10.0 is float"
echo $intAndFloat & " is " & intAndFloat.type.name
var a: int64 = 5
var b: int32 = 5
var int64Andint32 = myadd(a, b)
# echos "10 is int64"
echo $int64Andint32 & " is " & int64Andint32.type.name
Would you mind to give a more concrete example with your "generic container types"?
First of all: many thanks! This is a great community and I really appreciate your help
I have tried both suggested solutions For the idea from @gneu: You are right (and I have to say I am deeply impressed how much overview you have after 2 days)
import typetraits
type
CObj*[T] = object
wrapped*: T
proc createC[T](t : T) : CObj[T] =
result.wrapped=t
template add*[L,R](lhs: CObj[L], rhs: CObj[R]): auto =
createC(lhs.wrapped + rhs.wrapped)
var a=createC(1i32)
var b=createC(1'f64)
var c = a.add(b)
echo c.wrapped,":",c.wrapped.type.name
But this only works if the nim rules for type promotion are correct - I would need to handle cases like adding wrapped int32 and float64 which result in an Error: type mismatch: got (int32, float64) in the example above and I would also need to handle custom types like a self defined Complex32.
My second attempt was to use to use the solution proposed by @andrea which is precisely what I had in mind:
type
CObj*[T] = object
wrapped*: T
proc createC[T](t : T) : CObj[T] =
result.wrapped=t
proc f(L, R: typedesc): typedesc =
when L is int32 and R is int64: int32
when L is int32 and R is float64: float64
proc add*[L,R](lhs: CObj[L], rhs: CObj[R]): f(L,R) =
type(result)(lhs.wrapped + rhs.wrapped)
var a=createC[int32](1i32)
var b=createC[float64](1'f64)
var c = a.add(b)
echo c.wrapped,":",c.wrapped.type.name
I hope I got the syntax right but the result was Error: cannot evaluate at compile time: L - and I have to admit I am not sure if I referenced the result type correctly
From my (probably naive) point of view a solution like the one suggested by @andrea would be the one that feels the most direct - could I accomplish anything like this with macros?
Ok, seems like I just did not test the cases that are not working ;)
I think it has nothing to do with the wrapped types or the template at all. It is just the + operator that makes problems. And now things are getting really strange:
import typetraits
template myadd[L,R](lhs: L, rhs: R): auto =
echo "lhs is " & $lhs.type.name
echo "rhs is " & $rhs.type.name
lhs # with this line uncommented and the next line commented, the code compiles
#lhs + rhs # with this line uncommented and the previous line commented, the second call to myadd gives an error
var r1 = myadd(2, 2.0) # => "lhs is int" and "rhs is float64" -> works with lhs+rhs in myadd
var ai = 2
var bf = 2.0
var r2 = myadd(ai, bf) # => "lhs is int" and "rhs is float64" -> error with lhs+rhs in myadd (type mismatch: got (int, float64))
So at runtime, myadd tells me for both calls that it gets int and float64. However, if I uncomment the line "lhs+rhs" in myadd, then the compiler crashes in the second call to myadd (ONLY the second). At this point, I think someone with a little knowledge about how the compiler works should explain this behavior...
EDIT: Obviously, in the first case, the compiler applies an automatic type conversion from int to float64 for lhs in the expression "lhs+rhs"? My first thought was maybe because in the first case I'm passing an rvalue and in the second an lvalue. But this should make no difference, because they are copied to myadd (or are they moved, if they are rvalues (would make sense I think)? )? (-> Yes, on a second thought, I think it might be reasonable that the rvalue automatically gets the correct type, because as an rvalue, it is not really bound to a type?)
Of course, you could make the code work by defining all the missing + operators, but then you are back with explicitly defining them for all possible permutations by hand. Add the following snippet to my above code and it works for both calls:
proc `+`(a: int, b:float64): float64 =
float64(a) + b