0

I'm working on one of our system applications, specifically in the configuration file handling bits. We currently have 3 different places where a configuration file can be stored, and that can possibly be extended later. What I'm trying to do is simplify the way we need to add a new managed field.

The solution I have so far looks something like this:

package main

import (
    "reflect"
    "strconv"
    "strings"
)

type Datastore interface{}

type MyInt struct {
    intVal int
}

func NewMyInt(key string, dv int, db *Datastore) *MyInt {
    // Do something here to construct MyInt
    return &MyInt{intVal: dv}
}

type Config struct {
    myInts map[string]*MyInt

    // Tag is of form "<key in DB>:<default value>"
    Value1 MyInt "value1_key:12345"
    Value2 MyInt "value2_key:54321"
}

func NewConfig(db *Datastore) *Config {
    c := &Config{
        myInts: make(map[string]*MyInt),
    }

    cType := reflect.TypeOf(c)

    for i := 0; i < cType.NumField(); i++ {
        f := cType.Field(i)
        if f.Name == "myInts" {
            continue
        }

        tag := string(f.Tag)
        fields := strings.Split(tag, ":")

        switch f.Type.Name() {
        case "myInt":
            intVal, _ := strconv.Atoi(fields[1])
            val := NewMyInt(fields[0], intVal, db)
            c.myInts[fields[0]] = val

            // How do I set the i'th field to this newly constructed value?
        }
    }

    return c
}

So far I'm just missing this piece to do the assignment.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Ken P
  • 552
  • 3
  • 11
  • 1
    To see how to use `reflect` to set a struct field, see [peterSO's answer to this question](https://stackoverflow.com/q/6395076/1256452). Note though that using the type's *name* to decide what type to set it to is ... unusual, at least; normally you would switch on the *type* and use `case reflect.TypeOf(...)`, as in the example. If you have a set "I assume name X is type Y" you don't need to walk through the fields one at a time, as in the simplified version of that example. – torek Aug 03 '20 at 22:04

1 Answers1

0

For this question, you can try


func NewConfig(db *Datastore) *Config {
    c := &Config{
        myInts: make(map[string]*MyInt),
    }

    cType := reflect.TypeOf(c).Elem()  // have to use Elem() to get actual value
    cValue := reflect.ValueOf(c).Elem()

    for i := 0; i < cType.NumField(); i++ {
        f := cType.Field(i)
        if f.Name == "myInts" {
            continue
        }

        tag := string(f.Tag)
        fields := strings.Split(tag, ":")
        switch f.Type.Name() {
        case "MyInt":
            intVal, _ := strconv.Atoi(fields[1])
            val := NewMyInt(fields[0], intVal, db)
            c.myInts[fields[0]] = val

            // How do I set the i'th field to this newly constructed value?
            cValue.Field(i).Set(reflect.ValueOf(val).Elem())
        }

    }
    fmt.Println(c.Value1.intVal, c.Value2.intVal)

    return c
}