1

Does go allow you to instantiate a struct from a string? (similar to ruby convert class name in string to actual class in Ruby)

I am trying to separate code from a single file into subdirectories but was getting undefined: PngConverter when running go build. It may have been a load order issue however now I'm trying to dynamically instantiate the struct.

convert_image.go

var converters = map[string]string{
  "png": "PngConverter",
  "jpg": "JpegConverter",
  "jpeg": "JpegConverter",
}

type Converter interface {
  convert(*bytes.Buffer) string
}

func selectConverter(converterName string) {
  // dynamically initialize the converter with reflect?
}

func ConvertImageToWords(filename string, image *bytes.Buffer) string {
  ext = Ext(filename)
  if converterName, y := converters[ext]; y {
    converter = selectConverter(converterName)
    id = converter.convert(image)
  }

  return nil
}

converters/png_converter.go

type PngConverter struct {}

func (converter PngConverter) convert(in *bytes.Buffer) string {
  // ...
}

converters/jpeg_converter.go

type JpegConverter struct {}

func (converter JpegConverter) convert(in *bytes.Buffer) string {
  // ...
}
Community
  • 1
  • 1
Steve
  • 177
  • 1
  • 1
  • 7
  • Duplicate. Use a registry like package image does. – Volker Mar 09 '17 at 08:24
  • @Volker The image package doesn't identify objects in an image the same way that deep learning does. – Steve Mar 09 '17 at 08:41
  • Yes, this is true. And has nothing to do with my comment which is about how to allow different packages to register type into a other packages registry (map from string to actual objects/function/methods). – Volker Mar 09 '17 at 10:03

2 Answers2

1

Dynamic evaluation

Go is compiling language it means there is no dynamic evaluation, everything is done after compilation. Interpreting languages like Ruby, Python or Javascript has eval (and analogs) functions for dynamic programming which is conceptually impossible for compiling languages.

Interfaces as an idiomatic way of abstraction

If you need a generic type in Go - use interface with no doubt. Also you are very close to a correct approach: you don't need to evaluate a struct from string instead of it you should declare a map of object of Converter interface types with all required implementations.

Compilable playground:

var converters map[string]Converter

type Converter interface {
    convert(*bytes.Buffer) string
}

type PngConverter struct{}
type JpgConverter struct{}

func (p *PngConverter) convert(b *bytes.Buffer) (repr string) { return }
func (p *JpgConverter) convert(b *bytes.Buffer) (repr string) { return }

func ConvertImageToWords(
        filename string,
        image *bytes.Buffer,
        converters map[string]Converter) (repr string) {
    ext := Ext(filename)
    if converter, y := converters[ext]; y {
        repr = converter.convert(image)
    }

    return
}

Import and packages

You haven't been asked but this is wrong design:

Yes, they're in a separate directories...

Package is a namespace where you can have multiple files and you don't need to import each of them to another. Package is a directory with go files containing package <name> as a first line. But if you separated files with different packages (directories) you need to import it if you need to use it in another package. Java-like class-module design is not correct for Go. Just keep all your converters and the interface in the same package.

I159
  • 29,741
  • 31
  • 97
  • 132
0

No, you can't initialize a struct from String representations.

Reference

What you can do is pass the interface in ConvertImageToWords.

func ConvertImageToWords(conv Converter, image *bytes.Buffer) string

Or have the filename converted to interface Converter within ConvertImageToWords.

func GetConverter(ext string) Converter {
  switch ext {
    case "png":
      return PngConverter{}
    case "jpg":
   ...
}

func ConvertImageToWords(filename string, image *bytes.Buffer) string {
  ext = Ext(filename)
  conv := GetConverter(ext)
  id := conv.convert(image)
  ...
}

Also it's better to have the interface method exported :

type Converter interface {
  Convert(*bytes.Buffer) string
}

Reference

Community
  • 1
  • 1
John S Perayil
  • 6,001
  • 1
  • 32
  • 47
  • I tried this but now I'm getting `undefined: PngConverter` again since `PngConverter` is in a different file. Is there a way to import `PngConverter` into the main file? – Steve Mar 09 '17 at 08:24
  • @Steve Is the file in a different directory ? In go, different directories are separate packages, files within the same directory can use each other's types directly. [Refer](https://golang.org/doc/code.html#Organization) – John S Perayil Mar 09 '17 at 08:28
  • Yes, they're in a separate directories: `converters/png_converter.go`, `converters/jpg_converter.go`, `converters/webm_converter.go` – Steve Mar 09 '17 at 08:31
  • @Steve You need to go through the [link](https://golang.org/doc/code.html#Library) I referenced earlier, since your problem is beyond the scope of this answer / comments. You need to familiarise yourself with how Go Development is done. – John S Perayil Mar 09 '17 at 09:25