Thanks @Araq for your help. I found the `signatureHash` macro by investigating 
how symbol names are generated, and tried using it to create stable hashes of 
types across my DLLs, but for some reason it would sometimes generate a 
different hash for the same type used in different DLLs. I saw this PR which 
might help, but I'm unsure if it does what I think it does: 
<https://github.com/nim-lang/Nim/pull/13305>.

For now I ended up going with something similar to @enthus1ast's suggestion. 
Below is the PoC implementation. It generates sequential IDs based on a string 
hash of a type. It can be used across multiple DLL boundaries by assigning 
memory to `typeRegistry.hashes` allocated by the main executable.
    
    
    import std/macros
    
    type
      TypeId* = uint16
      
      TypeRegistry* = object
        hashes: ptr[TypeHashArray]
      
      TypeHashArray = array[TypeId.high, TypeHash]
      TypeHash = array[32, char]
    
    proc init*(self: var TypeRegistry) {.ctor.} =
      self.hashes = createShared TypeHashArray
    
    proc deinit*(self: var TypeRegistry) =
      if self.hashes != nil:
        dealloc self.hashes
        self.hashes = nil
    
    var typeRegistry*: TypeRegistry
    
    proc setTypeRegistry*(registry: TypeRegistry) {.dllapi.} =
      typeRegistry = registry
    
    proc getTypeHash(hash: string): TypeHash {.compileTime.} =
      for i, element in enumerate hash:
        result[i] = element
    
    macro typeHash(Type: type): TypeHash =
      # TODO: Make this more reliable (`signatureHash` fails sometimes)
      # Wait for https://github.com/nim-lang/Nim/pull/13305
      newCall(bindSym"getTypeHash", newLit $Type.getTypeImpl[1])
    
    proc getId(typeHash: static[TypeHash]): TypeId {.noinit.} =
      var id: TypeId
      
      while (let hash = typeRegistry.hashes[id] ; hash[0] != '\0'):
        if hash == typeHash:
          return id
        
        id += 1
      
      typeRegistry.hashes[id] = typeHash
      
      return id
    
    # fast memoized type ID; doesn't use `Tables`
    proc typeId*(Type: type): TypeId {.noinit.} =
      var typeId {.global.} : TypeId
      
      var implId {.global.} = 0
      var impl {.global.} = [
        proc: TypeId {.inline noinit.} =
          typeId = getId Type.typeHash
          result = typeId
          implId += 1,
        proc: TypeId {.inline noinit.} = typeId
      ]
      
      result = impl[implId]()
    
    
    Run

I hope someone finds something useful in that.

Reply via email to