1

I want to specify a function based on a string. I'm getting strings out of a map, in the example below they are the values in function while interating ove the map. Now for example, when the string value function == "networkInfo", I would like to "treat" that value as a real functions' name. It's hard to explain, but I think you guys will know what I mean.

My goal is to remove the switch statement and directly call c.AddFunc(spec, func() { networkInfo() }) where networkInfo is the function name, extracted from string function. I know this is possible, but I don't know how :(. Help is appreciated!

// ScheduleCronjobs starts the scheduler
func ScheduleCronjobs() {

tasks := props.P.GetStringMapString("tasks")
log.Infof("number of tasks: %d", len(tasks))

if len(tasks) != 0 {
    c := cron.New()

    // for each task, initialize
    for function, spec := range tasks {
        switch function {
        case "networkInfo":
            c.AddFunc(spec, func() { networkInfo() })
        case "bla":
            c.AddFunc(spec, func() { bla() })
        default:
            log.Errorf("unknown task: %q", function)
        }
    }
    c.Start()
}

// after initialization, send out confirmation message
slack.SendMessage("tasks initialized", props.P.GetString("channel"))
}
4
  • I assume all funcs have the same signature? i.e. func() Commented Mar 26, 2016 at 19:08
  • Also, do all the funcs live in the same package? Commented Mar 26, 2016 at 19:11
  • Yes, all functions (all "tasks") have the same signature. And all functions live in the same package. Commented Mar 26, 2016 at 19:18
  • Updated my answer with an explanation why your request to find a func by name using reflection wouldn't work. Commented Mar 26, 2016 at 20:07

1 Answer 1

3

Why not something like:

taskDefs := map[string]func(){
  "networkInfo": networkInfo,
  "bla": bla,
}

for function, spec := range tasks {
  if fn, ok := taskDefs[function]; ok {
    c.AddFunc(spec, func() { fn() }) // not sure if you need the enclosing func
  } else {
    log.Errorf("unknown task: %q", function)
  }
}

If you do need varying signatures of your funcs then you'd actually need reflection, but if the types of the funcs are all the same, then using this map approach might be a simpler solution, without the overhead of reflection.

The only way I've found to find functions by name in a package is by actually parsing the source files. This repo is an example of finding funcs and storing them in a map with the name as the key.

The Go linker will silently drop unreferenced funcs, so if the only way you're referencing the func is through reflection it would break. That is why the map approach I suggest is superior; it let's the linker know the func is being used.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your answers, they really help me a lot. In your suggestion, I see a taskDefs map[string]func() where all "tasks" need to be defined. Isn't is possible to skip this part? This way I can add tasks by simply putting the function name in a config file and implement the function in my tasks package, without having to add something to taskDefs.
@RogierLommers I understand what you're wanting; to avoid the explicit mapping between name and function. There are two problems with this: 1) I don't see a way to do it through reflection, unless you dynamically read the source with the go parser at runtime, which seems way heavier of an approach to do the same thing ultimately (map of name to function). 2) The go linker will drop unreferenced functions, so there is a risk that the function won't even be in the final binary if it's not referenced anywhere else, meaning it won't work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.