Below is a way to access tuple and object fields by name. Something that would 
be convenient from time to time. There was some posts on it before which lead 
me to using fieldPairs. I tried applying generics to make it more "universal". 
Aside from the efficiency issue of iterating through the fields each time, is 
there a reason this should not be used?
    
    
    type FieldTypeError* = object of FieldDefect
    
    proc setField*[S:tuple|object; T](tp:var S; fld:string; val:T) =
      for k,v in tp.fieldPairs:
        when (typeOf(T) is typeOf(v)):
          if k == fld:
            v = val
            return
      raise newException(FieldTypeError, "A field named " & fld & " of type " & 
$typeOf(val) & " not found")
    
    proc setField*[S:ref tuple|ref object; T](tp:var S; fld:string; val:T) =
      tp[].setField(fld, val)
    
    
    proc getField*[S:tuple|object; T](tp:S; fld:string; def:typedesc[T]):T =
      for k,v in tp.fieldPairs:
        when (typeOf(T) is typeOf(v)):
          if k == fld:
            return v
      raise newException(FieldTypeError, "A field named " & fld & " of type " & 
$typeOf(def) & " not found")
    
    proc getField*[S:ref tuple|ref object; T](tp:S; fld:string; 
def:typedesc[T]):T =
      tp[].getField(fld, def)
    
    
    when isMainModule:
      
      type
        TPerson = tuple[name:string, num:int, tf:bool]
        OPerson = object
          name:string
          num:int
          tf:bool
        RPerson = ref OPerson
      
      var p1:TPerson  = (name:"Alpha", num:0, tf:false)
      doAssert p1 == (name: "Alpha", num: 0, tf: false)
      p1.setField("name", "Beta")
      doAssert p1 == (name: "Beta", num: 0, tf: false)
      p1.setField("num", 1)
      doAssert p1 == (name: "Beta", num: 1, tf: false)
      p1.setField("tf", true)
      doAssert p1 == (name: "Beta", num: 1, tf: true)
      try:
        p1.setField("nothing", "Nothing")
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected - No field named "nothing"
      try:
        p1.setField("name", 42)
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected - "name" should be string, not int
      
      var p2 = OPerson(name:"Charlie", num:2)
      doAssert $p2 == """(name: "Charlie", num: 2, tf: false)"""
      p2.setField("name", "Delta")
      doAssert $p2 == """(name: "Delta", num: 2, tf: false)"""
      p2.setField("num", 3)
      doAssert $p2 == """(name: "Delta", num: 3, tf: false)"""
      p2.setField("tf", true)
      doAssert $p2 == """(name: "Delta", num: 3, tf: true)"""
      try:
        p2.setField("nothing", "Nothing")
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected - No field named "nothing"
      try:
        p2.setField("name", 42)
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected
      
      var p3 = new RPerson
      p3.name = "Echo"
      p3.num = 4
      doAssert $p3[] == """(name: "Echo", num: 4, tf: false)"""
      p3.setField("name", "Foxtrot")
      doAssert $p3[] == """(name: "Foxtrot", num: 4, tf: false)"""
      p3.setField("num", 5)
      doAssert $p3[] == """(name: "Foxtrot", num: 5, tf: false)"""
      p3.setField("tf", true)
      doAssert $p3[] == """(name: "Foxtrot", num: 5, tf: true)"""
      try:
        p3.setField("nothing", "Nothing")
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected - No field named "nothing"
      try:
        p3.setField("name", 42)
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected
      
      doAssert p1.getField("name",string) == "Beta"
      doAssert p1.getField("num",int) == 1
      doAssert p1.getField("tf",bool) == true
      try:
        doAssert p1.getField("nothing",string) == "Nothing"
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected
      #doAssert p1.getField("wrong", string) == 5   # Compile error - Type 
Mismatch
      
      doAssert p2.getField("name",string) == "Delta"
      doAssert p2.getField("num",int) == 3
      doAssert p2.getField("tf",bool) == true
      try:
        doAssert p3.getField("nothing",int) == 42
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected
      #doAssert p3.getField("wrong",int) == false  # Compile error - Type 
Mismatch
      
      doAssert p3.getField("name",string) == "Foxtrot"
      doAssert p3.getField("num",int) == 5
      doAssert p3.getField("tf",bool) == true
      try:
        doAssert p3.getField("nothing",bool) == false
        echo "Should have raised FieldTypeError"
      except FieldTypeError:
        discard # This is expected
      #doAssert p3.getField("wrong",bool) == "false"  # Compile error - Type 
Mismatch
    
    
    Run

Reply via email to