To make sure the messages in my message-queue are type-safe, I have to record the message type in each message.
This is how I went about it:
proc idOfType*(T: typedesc): MsgTypeID {.inline.} =
let tid = 0 # Accesses "type register here" ...
MsgTypeID(tid)
proc sendMsg*[T](q: QueueID, m: ptr Msg[T]): void {.inline.} =
let typeidOfT {.global.} = idOfType(T)
m.mybase.mytypeid = typeidOfT
fillAndSendMsg[T](q, m, true)
It looked like it worked, but then today I realised I was using a single type in the test messages, so I added a second type to my test, and then it failed. To get it to work again, I have to change the code to this:
proc idOfType*(T: typedesc): MsgTypeID {.inline.} =
let tid = 0 # Accesses "type register here" ...
MsgTypeID(tid)
proc sendMsg*[T](q: QueueID, m: ptr Msg[T]): void {.inline.} =
let typeidOfT = idOfType(T)
m.mybase.mytypeid = typeidOfT
fillAndSendMsg[T](q, m, true)
I was under the impressing that since sendMsg() is generic, the compiler would generate one typeidOfT per type T. Since my test is now failing with two types, I assume the compiler generates a single typeidOfT for all sendMsg() "instantiations", initialised the first time sendMsg() is called.
Is there a way to work around that? It's rather expensive to compute the ID of a type (it involves locks and creating shared heap objects), so I don't want to do that for each message, but rather only once for each type.
Your example seems to work fine with 0.17.2, idOfType is only called once per T
import typetraits
type
Message[T] = object
proc idOfType*(T: typedesc): int {.inline.} =
echo("Getting Id of type ", T.name)
when T is int:
0
else:
1
proc sendMsg*[T](m: ptr Message[T]): void {.inline.} =
let typeIdOfT {.global.} = idOfType(T)
echo("Sending message with type Id ", typeIdOfT)
proc test() =
var intMsg: Message[int]
var stringMsg: Message[string]
sendMsg(addr(intMsg))
sendMsg(addr(stringMsg))
sendMsg(addr(intMsg))
sendMsg(addr(stringMsg))
test()
Output:
Getting Id of type int
Getting Id of type string
Sending message with type Id 0
Sending message with type Id 1
Sending message with type Id 0
Sending message with type Id 1
Sounds like a bug, I think. What version of the compiler are you using?
Hi, I'm also using 0.17.2. Maybe I interpreted the problem incorrectly... I'll use your example to check if it behaves differently in my code.
EDIT: Your example works for me too. But using your simplified "idOfType()" in my code still produces the same error. I checked the generated C code; there are two definitions of "sendMsg()", one for "sendMsgint" and one for "sendMsgfloat", but they both use the same "typeidOfT", resulting in my test failure. I'll have to create an example that reproduces the error.
EDIT2: There is definetly a compiler bug here (validated by looking at the generated C code). And I know exactly which line of my code produces it. But After trying for several hours, it seems impossible (for me) to reproduce in a simple test case taht reproduces the issue.
I have a method that looks about like this:
proc replyWith*[Q,A](request: ptr Message[Q], reply: ptr Message[A]): void =
assert(request != nil, "request cannot be nil")
assert(reply != nil, "reply cannot be nil")
reply.q = request.q
sendMsg(reply.q, reply)
And if I call it, the compiler stops producing separate typeIdOfT per type, and reuse the same typeIdOfT in all calls to sendMsg().
I have eventually found a work-around. I rename idOfType() to idOfType2(). Make it "private". Creates a new idOfType() like this:
proc idOfType*(T: typedesc): MsgTypeID {.inline.} =
## Returns the ID of a type.
let typeidOfT {.global.} = idOfType2(T)
result = typeidOfT
And remove the {.global.} in sendMsg(). Like that it is even slightly faster, since I also call idOfType() directly in a few place. But it's just a work-around. And without a simple reproducible example, there is little value in posting an issue in Github.