Skip to content
This repository has been archived by the owner on May 8, 2019. It is now read-only.

Using the Code Generator

dchenk edited this page Apr 18, 2018 · 3 revisions

The msgp code generator takes Go code as input and creates files with more Go code as output. The msgp command takes the type definitions in a particular Go source file and outputs a number of methods for that type in order to serialize it to or from MessagePack.

By default, the generator generates implementations for all of the following interfaces:

  • msgp.Marshaler
  • msgp.Unmarshaler
  • msgp.Sizer
  • msgp.Decoder
  • msgp.Encoder

Additionally, the generator will generate tests and benchmarks for the implemented methods. You can turn the generation of particular method sets and tests on and off with flags passed to the generator.

Using the Generator with go generate

If you want to run the generator with the default options, all you need to include in your source file is one commented line:

//go:generate msgp

Options

The following flags are supported:

  • -o - output file name (default is {input}_gen.go)
  • -src - input file name or directory (default is $GOFILE set by the go generate command)
  • -io - satisfy the msgp.Decoder and msgp.Encoder interfaces (default is true)
  • -marshal - satisfy the msgp.Marshaler and msgp.Unmarshaler interfaces (default is true)
  • -tests - generate tests and benchmarks (default is true)

Provided that you have msgp and the go generate tool installed, all you should have to do is run go generate in the same directory as your source file to invoke the generator.

For example, if you want the output file to be called stuff.go and you don't want to satisfy the msgp.Decoder and msgp.Encoder interfaces or generate tests, you would instead use:

//go:generate msgp -o=stuff.go -io=false -tests=false

Invoking the Generator Manually

If you want to run the generator without using go generate, you must use the -src flag. If you wanted to run the generator on the file my_types.go, then from the source directory you would run (assuming msgp is installed and in your $PATH):

msgp -src=my_types.go

which would generate my_types_gen.go and my_types_gen_test.go.

Using Directives

You can adjust how the code generator generates methods using directives as source code comments. All directives have the form:

//msgp:directive [arg1] [arg2] [arg3]...

There are two kinds of directives: global and pass-specific.

Global Directives

Ignore
//msgp:ignore Type1 Type2 Type3

The ignore directive tells the code generator to ignore the types listed.

Tuple
//msgp:tuple TypeA

type TypeA struct {
    Left  float64
    Right float64
}

The msgp:tuple directive tells the generator to generate code for the struct so that it is serialized as an array instead of a map. In other words, TypeA{1.0, 2.0} above would be encoded as

[1.0,2.0]

instead of

{"Left":1.0,"Right":2.0}

For smaller objects, tuple encoding can yield serious performance improvements.

Shim
//go:generate msgp

//msgp:shim Enum as:string using:(Enum).String/parseString mode:convert

type Enum byte

const(
    A Enum = iota
    B
    C
    D
    invalid
)

func (e Enum) String() string {
    switch e {
    case A:
        return "A"
    case B:
        return "B"
    case C:
        return "C"
    case D:
        return "D"
    default:
        return "<invalid>"
    }
}

func parseString(s string) Enum {
    switch s {
    case "A":
        return A
    case "B":
        return B
    case "C":
        return C
    case "D":
        return D
    default:
        return invalid
    }
}

The shim directive lets you inline a type-conversion function for a user-defined type in order to have it encode and decode differently than the default for its concrete type. In the example above, we're using the shim directive to translate a const-iota block into strings for encoding and decoding. Note that the as: argument must take a "base" type (a built-in, []byte, interface{}, msgp.Extension or a type already processed by the code generator.) The mode can be either "convert" or "cast" and defaults to "cast" if not set.

Pass-Specific Directives

A pass-specific directive operates on one particular code generation pass. They have the form:

//msgp:passname directives [arg1] [arg2] ...

The valid pass names are:

  • encode
  • decode
  • marshal
  • unmarshal
  • size
  • test
Ignore

The ignore directive can be applied to a particular combination of pass and type when invoked like:

//msgp:encode ignore Type1 Type2...

Methods that are ignored also do not produce test cases. (The code generator is aware of the dependency.)

Ignore can be particularly useful when you don't want the code generator to clobber an existing manually-written implementation of one of the methods.

Matching Type Names

In any directive, along with the concrete names of types you can list regular expressions to match types. Simply prefix reg= before the regular expression to match, or prefix reg!= before the regular expression that the type names should not match. For example, this set of directives:

//go:generate msgp
//msgp:encode ignore reg=User
//msgp:unmarshal ignore reg!=^Contents

indicate that types that include User in the name will not get an EncodeMsg method, and only types that begin with the string Contents will get an UnmarshalMsg method.

You can use concrete type names along with regexp matching: //msgp:encode ignore reg=Req AnotherType

Using a flag like -io or -marshal may not be enough for you if, for example, in general you don't want methods of a particular kind but do want it for only certain types.

The regular expression syntax is described here: https://godoc.org/regexp