9

I'm trying to build an easy to use templating system. Basically I just want to create a slice with different variables ( strings ) and then loop through the slice and replace the markup {{}} with the actual values. So if the variable 'name' is onevar it will look in the template for {{onevar}} and replace that with the actual value of the variable .

Question: how do I get the variable name? Basically what's in the source code. Is it possible ? I've tried something with reflect but seems I couldn't get it right. See belowg

onevar := "something"
other := "something else"

var msg string
    sa := []string{onevar, other}
    for _, v := range sa {
        vName := reflect.TypeOf(v).Name()
        vName = fmt.Sprintf("{{%s}}", vName)
        msg = strings.Replace(msg, vName, v, -1)
    }
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
hey
  • 7,299
  • 14
  • 39
  • 57
  • 3
    Pretty sure it's not possible, you'd have to use a struct. – OneOfOne Jul 19 '14 at 04:19
  • yeah but I can't range on a struct... I've just tried to reduce the boilerplate i.e writing strings.Replace for each variable – hey Jul 19 '14 at 04:25
  • Well, you half way get variable names in a struct but yeah 99% sure it's not possible the way you're doing right now. – OneOfOne Jul 19 '14 at 04:27
  • I ended-up with a `map[string]string`. – hey Jul 19 '14 at 04:47
  • Django templating uses maps (Python dicts), so no crime for a Go system to do the same. You've got no idea how much you want to use html/template, though: it takes care of stuff like escaping for you and has loops and stuff and was built by the gopher-iest of Gophers. – twotwotwo Jul 19 '14 at 04:49

3 Answers3

3

You cannot do such stuff. The slice does not contain the variables but their values, so you cannot get their name. Just use a map.

Volker
  • 40,468
  • 7
  • 81
  • 87
  • To clarify: there is no Golang solution [like what C++ and Rust can do (apparently) do](https://stackoverflow.com/questions/65899214/how-do-i-print-variable-name-instead-of-the-value-assigned-to-the-variable/65899295#comment131567418_65899295)? – Johnny Utahh Nov 22 '22 at 12:48
2

instead of working with the variable names, you might work with a slice with (string converted) pointers to the variables to reach your original aim:

package main

import "fmt"
import "unsafe"
import "strconv"

func castStr(v *string) string {
    return fmt.Sprint(uintptr(unsafe.Pointer(v)))
}

func uncastStr(s string) string {
    p, _ := strconv.ParseInt(s, 10, 64)
    return *((*string)(unsafe.Pointer(uintptr(p))))
}

func main() {
    onevar := "something"
    other := "something else"
    sa := []string{castStr(&onevar), castStr(&other)}

    for _, v := range sa {
        fmt.Printf("{{%s}}\n", v)
        fmt.Printf("%v\n", uncastStr(v))
    }

    //for _, v := range sa {
    //  vName := fmt.Sprintf("{{%s}}", v)
    //  msg = strings.Replace(msg, vName, uncastStr(v) -1)
    //}
}

(Don't see a problem working with unsafe here because any casting is not being based of uncontrolled content and unsafe.Pointer is only used for reading. Attention!: Have in mind that pointer-values might vary between program runs. Thus, replacing the templates {{xxx}} in a second run of the program might fail. Moreover: that scenario (second run) might be "unsafe" since unrelated memory might be assessed.)

ABri
  • 610
  • 6
  • 16
-1

Its good question. I need same solution, but not for vars (i think it`s not possible) - for structs. You can try use structures for this.

package main

import (
    "fmt"
    "reflect"
)

type Some struct {
    Foo bool
    A   string
    Bar int
    B   string
}

func (structPoint *Some) GetFieldName(fieldPinter interface{}) (name string) {

    val := reflect.ValueOf(structPoint).Elem()
    val2 := reflect.ValueOf(fieldPinter).Elem()

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        if valueField.Addr().Interface() == val2.Addr().Interface() {
            return val.Type().Field(i).Name
        }
    }
    return
}

func main() {
    a := Some{}

    fieldName := a.GetFieldName(&a.Foo)
    fmt.Println(fieldName)

    fieldName = a.GetFieldName(&a.Bar)
    fmt.Println(fieldName)

    //Foo
    //Bar
}

https://play.golang.org/p/0duV6GY2NSG

tsv
  • 69
  • 5