I'm trying to create a small module that represents "strings" as "values" 
rather than "ref objects". I had this working for a "fixed size" type (254 
chars + "0" \+ "length field"), and now tried to define the size as a generic 
parameter.

First, I had issues trying to get this to compile:
    
    
    type
      StringValue*[LEN: Natural] = distinct array[LEN+2,char]
    

But the compiler refuses to do "LEN+2". I even tried to define a compile-time 
proc to add Naturals, but it still would not compile (did not seem to see my + 
proc). So what I then tried, was to replace "[LEN: Natural]" with "[LEN: 
static[Natural]]", and now the compiler crashes.

Here the stringvalue module, with no test code. It _seems_ to compile.
    
    
     # Module: stringvalue
    
    ## This module can be used to pass small "strings" across threads, or store
    ## them globally, without having to worry about the local GC (as Nim strings
    ## are local GCed objects). The "strings" are meant to be used as "values",
    ## rather than "reference objects", but can also be allocated on the shared
    ## heap.
    
    when isMainModule:
      echo("COMPILING StringValue ...")
    
    
    import hashes
    
    export hash, `==`
    
    proc c_strcmp(a, b: cstring): cint {.
      importc: "strcmp", header: "<string.h>", noSideEffect.}
    
    type
      StringValue*[LEN: static[Natural]] = distinct array[LEN+Natural(2),char]
        ## Represents a "string value" of up to 254 characters (excluding
        ## terminating '\0' and length).
    
    proc cstr*[LEN: static[Natural]](sv: var StringValue[LEN]): cstring 
{.inline, noSideEffect.} =
      ## Returns the 'raw' cstring of the StringValue
      result = cast[cstring](addr sv)
    
    proc `[]`*[LEN: static[Natural],I: Ordinal](sv: var StringValue[LEN]; i: 
I): char {.inline, noSideEffect.} =
      ## Returns a char of the StringValue
      cast[ptr char](cast[ByteAddress](addr sv) +% i * sizeof(char))[]
    
    proc `[]`*[LEN: static[Natural],I: Ordinal](sv: StringValue[LEN]; i: I): 
char {.inline, noSideEffect.} =
      ## Returns a char of the StringValue
      cast[ptr char](cast[ByteAddress](unsafeAddr sv) +% i * sizeof(char))[]
    
    proc `[]=`*[LEN: static[Natural],I: Ordinal](sv: var StringValue[LEN]; i: 
I; c: char) {.inline, noSideEffect.} =
      ## Returns a char of the StringValue
      cast[ptr char](cast[ByteAddress](addr sv) +% i * sizeof(char))[] = c
    
    proc len*[LEN: static[Natural]](sv: var StringValue[LEN]): int {.inline, 
noSideEffect.} =
      ## Returns the len of the StringValue
      int(uint8(sv[LEN+1]))
    
    proc len*[LEN: static[Natural]](sv: StringValue[LEN]): int {.inline, 
noSideEffect.} =
      ## Returns the len of the StringValue
      int(uint8(sv[LEN+1]))
    
    proc `$`*[LEN: static[Natural]](sv: var StringValue[LEN]): string 
{.inline.} =
      ## Returns the string representation of the StringValue
      result = $sv.cstr
    
    proc `$`*[LEN: static[Natural]](sv: StringValue[LEN]): string {.inline.} =
      ## Returns the string representation of the StringValue
      result = $sv.cstr
    
    proc hash*[LEN: static[Natural]](sv: var StringValue[LEN]): Hash {.inline, 
noSideEffect.} =
      ## Returns the hash of the StringValue
      result = hash($sv)
    
    proc hash*[LEN: static[Natural]](sv: StringValue[LEN]): Hash {.inline, 
noSideEffect.} =
      ## Returns the hash of the StringValue
      result = hash($sv)
    
    proc `==`*[LEN: static[Natural]](a, b: var StringValue[LEN]): bool 
{.inline, noSideEffect.} =
      ## Compares NoStrings
      (a.len == b.len) and (c_strcmp(a.cstr, b.cstr) == 0)
    
    proc `==`*[LEN: static[Natural]](a, b: StringValue[LEN]): bool {.inline, 
noSideEffect.} =
      ## Compares NoStrings
      result = false
      if a.len == b.len:
        result = (c_strcmp(a.cstr, b.cstr) == 0)
    
    proc `==`*[LEN: static[Natural]](sv: var StringValue[LEN], cs: cstring): 
bool {.inline, noSideEffect.} =
      ## Compares a StringValue to a cstring
      result = (cast[pointer](cs) != nil) and (c_strcmp(sv.cstr, cs) == 0)
    
    proc `==`*[LEN: static[Natural]](sv: StringValue[LEN], cs: cstring): bool 
{.inline, noSideEffect.} =
      ## Compares a StringValue to a cstring
      result = (cast[pointer](cs) != nil) and (c_strcmp(sv.cstr, cs) == 0)
    
    proc `==`*[LEN: static[Natural]](cs: cstring, sv: var StringValue[LEN]): 
bool {.inline, noSideEffect.} =
      ## Compares a cstring to a StringValue
      result = (cast[pointer](cs) != nil) and (c_strcmp(sv.cstr, cs) == 0)
    
    proc `==`*[LEN: static[Natural]](cs: cstring, sv: StringValue[LEN]): bool 
{.inline, noSideEffect.} =
      ## Compares a cstring to a StringValue
      result = (cast[pointer](cs) != nil) and (c_strcmp(sv.cstr, cs) == 0)
    
    proc `<`*[LEN: static[Natural]](a, b: var StringValue[LEN]): bool {.inline, 
noSideEffect.} =
      ## Compares NoStrings
      (c_strcmp(a.cstr, b.cstr) < 0)
    
    proc `<`*[LEN: static[Natural]](a, b: StringValue[LEN]): bool {.inline, 
noSideEffect.} =
      ## Compares NoStrings
      (c_strcmp(a.cstr, b.cstr) < 0)
    
    proc `<=`*[LEN: static[Natural]](a, b: var StringValue[LEN]): bool 
{.inline, noSideEffect.} =
      ## Compares NoStrings
      (c_strcmp(a.cstr, b.cstr) <= 0)
    
    proc `<=`*[LEN: static[Natural]](a, b: StringValue[LEN]): bool {.inline, 
noSideEffect.} =
      ## Compares NoStrings
      (c_strcmp(a.cstr, b.cstr) <= 0)
    
    proc initNoString*[LEN: static[Natural]](s: cstring): StringValue[LEN] 
{.inline, noSideEffect.} =
      ## Creates a StringValue
      static:
        assert(LEN <= Natural(254))
      let p = cast[pointer](s)
      let len = if p == nil: 0 else: len(s)
      if len > LEN:
        raise newException(Exception, "s is too big: " & $len)
      result[LEN+1] = char(len)
      if len == 0:
        result[0] = char(0)
      else:
        copyMem(result.cstr, p, len+1)
    
    converter toNoString*[LEN: static[Natural]](s: cstring): StringValue[LEN] 
{.inline, noSideEffect.} =
      ## Converts a cstring to a StringValue.
      result = initNoString(s)
    
    converter toNoString*[LEN: static[Natural]](s: string): StringValue[LEN] 
{.inline, noSideEffect.} =
      ## Converts a string to a StringValue.
      result = initNoString(s)
    

And here the test code, which crashes the compiler.
    
    
    import stringvalue
    
    type
      StringValue16* = StringValue[14]
        ## A 16-bytes StringValue (maximum length is 14).
      StringValue32* = StringValue[30]
        ## A 32-bytes StringValue (maximum length is 30).
      StringValue64* = StringValue[62]
        ## A 64-bytes StringValue (maximum length is 62).
      StringValue128* = StringValue[126]
        ## A 128-bytes StringValue (maximum length is 126).
      StringValue256* = StringValue[254]
        ## A 256-bytes StringValue (maximum length is 254).
    
    when isMainModule:
      echo("TESTING StringValue ...")
      let text1 = "abc"
      let text2 = "def"
      var st1 = initNoString[3](text1)
      var st2 = initNoString[3](text2)
      assert(st1.len == 3)
      assert(st1.cstr == text1.cstring)
      assert(st2.cstr == text2.cstring)
      assert(st1 == text1)
      assert(text1 == st1)
      assert($st1 == text1)
      assert(text1 == $st1)
      assert(st1 != st2)
      assert(st1 < st2)
      assert(st1 <= st2)
      assert(st2 > st1)
      assert(st2 >= st1)
      assert(st1[1] == 'b')
      assert(hash(st1) == hash(text1))
      var st3: StringValue = "abc"
      assert(st3 == text1)
      st3[0] = 'd'
      st3[1] = 'e'
      st3[2] = 'f'
      assert(st3 == text2)
    

I'm using nim 0.17.2 on Windows 10, and vcc:

> Visual Studio 2017 Developer Command Prompt v15.0.26228.13
> 
> Copyright (c) 2017 Microsoft Corporation
> 
> [vcvarsall.bat] Environment initialized for: 'x64'

I think the main issue is (beyond the compiler crash), how to get this to 
compile:
    
    
    type
      StringValue*[LEN: Natural] = distinct array[LEN+2,char]
    

Reply via email to