It sounds reasonable that the discriminator of an object variant should be kept 
invariable to keep the object's data structure safe; I found, however, some 
scenarios in which it can be done. One way was to use the compiler directive 
--fieldChecks:off. I also attempted the same, but using the pragma 
{.fieldChecks: off.} and it worked. In fact I just found about that pragma by 
intuition, as it's not documented anywhere as far as I could research. I learnt 
also that you can use the pragma {.fieldChecks: on.} to bring back checking 
after the "unsafe" procedures are declared. I assume that "unsafe" procedures 
are all those that may use the discriminator once it has been changed.

For example, in the code below,
    
    
    type
      
      MyKind = enum mk1, mk2
      
      MyObj = object
        case mk: MyKind:
          of mk1: x, y, z: float
          of mk2: u, v, w: float
    
    proc myProc(mk: MyKind; i, j, k: float): MyObj =
      case mk:
        of mk1: return MyObj(mk: mk1, x: i, y: j, z: k)
        of mk2: return MyObj(mk: mk2, u: i, v: j, w: k)
    
    {.fieldChecks: off.} # disable field checks for object variants
    proc change(mo: var MyObj; mkTo: MyKind) =
      # this is an unsafe proc that changes the discriminator
      # so the fieldChecks must me turned off to avoid runtime errors
      mo.mk = mkTo
    {.fieldChecks: on.} # turn field checks back on
    
    {.fieldChecks: off.} # disable field checks again, this proc `$` seems to 
fail at runtime otherwise
    proc `$`(mo: MyObj): string =
      # seemingly because of the previous proc, `$` also needs fieldChecks off
      # this can be made public* as it is safe to use
      result = "[mk: " & $mo.mk & "]:("
      case mo.mk:
        of mk1: result &= "x:" & $mo.x & ", y:" & $mo.y & ", z:" & $mo.z
        of mk2: result &= "u:" & $mo.x & ", v:" & $mo.y & ", w:" & $mo.z
      result &= ")"
    {.fieldChecks: on.} # turn field checks back on
    
    proc rotate(mo: var MyObj) =
      # rotates the contents of mo regardless of what kind it is
      # this proc seems not to require fieldChecks turned off
      var
        temp: float
        orik: MyKind = mo.mk # this is the original kind
      change(mo, mk1) # temporary kind change
      # all kinds are treated as mk1, so less code is required
      temp = mo.x
      mo.x = mo.y
      mo.y = mo.z
      mo.z = temp
      change(mo, orik) # kind back to the original kind
    
    var
      p, q: MyObj
    
    p = myProc(mk1, 1, 2, 3)  # p starts as kind "mk1"
    echo "p = ", p            # output: p = [mk: mk1](x:1.0, y:2.0, z:3.0)
    change(p, mk2)            # p is changed to kind "mk2"
    echo "p = ", p            # output: p = [mk: mk2](u:1.0, v:2.0, w:3.0)
    rotate(p)                 # parameters of P are rotated
    echo "p = ", p            # output: p = [mk: mk2](u:1.0, v:2.0, w:3.0)
    q = myProc(mk2, 4, 5, 6)  # q starts as kind "mk2"
    echo "q = ", q            # output: q = [mk: mk2](u:4.0, v:5.0, w:6.0)
    rotate(q)                 # parameters of q are rotated
    echo "q = ", q            # output: q = [mk: mk2](u:5.0, v:6.0, w:4.0)
    
    
    
    Run

Reply via email to