Steve
Steve

Reputation: 317

How to instantatiate struct from the string representation of the struct name

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 {
  // ...
}

Upvotes: 2

Views: 709

Answers (2)

I159
I159

Reputation: 31109

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.

Upvotes: 1

John S Perayil
John S Perayil

Reputation: 6335

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

Upvotes: 0

Related Questions