Skip to content

Preprocessor Directives

Klaus Post edited this page Nov 21, 2024 · 6 revisions

You can make tweaks to how the code generator generates methods using directives in your source code comments. All directives take the form

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

much like the gc compiler directives.

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

Global Directives

Ignore

//msgp:ignore Type1 Type2 Type3

Ignore tells the code generator not to generate methods for the list of types supplied.

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

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

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.)

Replace

the replace directive makes it easier to serialize foreign types.

Example usage with github.com/google/uuid

  package main

  import "github.com/google/uuid"

  //go:generate msgp

  //msgp:replace uuid.UUID with:UUID
  type UUID [16]byte

  // Or like that
  //msgp:replace uuid.UUID with:[16]byte

  type User struct {
    ID uuid.UUID
  }

This can also be used to replace serialization of external types with internal types.

For example //msgp:replace cpu.TimesStat with:cpuTimesStat will replace an external type cpu.TimesStat with a local cpuTimesStat, which should be a copy of the external struct, that has generated functions.

Pass-Specific Directives

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

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

The valid pass names are as follows:

  • 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.