(asked this some time ago on #Nim, but still haven't found a solution)
I'm trying to assign some data to a type, which can then be accessed by a generic instantiation using the specific type. Here is what I'm trying to do (but does not compile).
import msgpack4nim
import strutils
import typetraits
type
MsgAlpha = object
id: int
x, y: int
url: string
MsgBeta = object
id: int
resource: string
# ...
const info = {
MsgAlpha: (id: 1),
MsgBeta: (id: 2),
# ...
}
proc serialize[T](msg: T) =
var msgcopy = msg
msgcopy.id = info[msg.type].id # Too bad!
echo msgcopy.pack.stringify
serialize MsgAlpha(x: 10, y: 30, url: "http://foo.bar")
serialize MsgBeta(resource: "GLES2")
My goal is to do serialization of protocol data where the caller does not have to know the details of the message being serialized (in this example the "id" field, in real life there is more message specific data). The culprit is the line marked # Too bad!. Here I'd like to get the data associated to the type T of msg.
The const info above compiles, but Nim does not seem to be sure of its type. When asked (typetraits) it tells me array[0..1, tuple of (type MsgAlpha, tuple[id: int])], which is almost right, but not quite.
Is there a way to do this without referencing to type names as strings?
Type cannot be used as values in Nim.
What you should do is something like this:
import msgpack4nim
import strutils
import typetraits
type
MsgKind = enum
MsgAlpha, MsgBeta
Msg = object
id: int
case kind: MsgKind
of MsgAlpha:
x, y: int
url: string
of MsgBeta:
resource: string
# ...
const info = {
MsgAlpha: (id: 1),
MsgBeta: (id: 2),
# ...
}
proc serialize(msg: Msg) =
var msgcopy = msg
msgcopy.id = info[msg.kind].id # Too bad!
echo msgcopy.pack.stringify
serialize Msg(kind: MsgAlpha, x: 10, y: 30, url: "http://foo.bar")
serialize Msg(kind: MsgBeta, resource: "GLES2")
You could use overloaded templates
type
MsgAlpha = object
id: int
x, y: int
url: string
MsgBeta = object
id: int
resource: string
template msgId(x: typedesc[MsgAlpha]): untyped = 1
template msgId(x: typedesc[MsgBeta]): untyped = 2
proc serialize[T](msg: T) =
var msgcopy = msg
msgcopy.id = msg.type.msgId
echo msgcopy
serialize MsgAlpha(x: 10, y: 30, url: "http://foo.bar")
serialize MsgBeta(resource: "GLES2")
:-) For this example as written, you could use (drum roll) overloaded procs:
type
MsgAlpha = object
id: int
x, y: int
url: string
MsgBeta = object
id: int
resource: string
proc msgId(m: MsgAlpha): int = 1
proc msgId(m: MsgBeta): int = 2
proc serialize[T](msg: T) =
var msgcopy = msg
msgcopy.id = msg.msgId
echo msgcopy
serialize MsgAlpha(x: 10, y: 30, url: "http://foo.bar")
serialize MsgBeta(resource: "GLES2")
I suspect you may be trying to do something more elaborate that isn't captured by your opening example here?
@deansher: Well, I was also looking for a way that would make the definition of messages nice-looking and trivial. The drum-roll-overloaded-procs are so simple I didn't even think of this, although I do not like the syntax for defining messages like this.
But this is Nim: macro -> DSL to the rescue, all solved. Thanks!
If you want to set them at the type definition you can use custom annotations
That's pretty much exactly what I need - thanks for pointing that out!
you can also hold data on types by using static though then it needs to be known at compileTime
type
typeWithData[data:static[auto]] = distinct object
var a:typeWithData[10]
echo a.data
#outputs 10