Skip to content

protoc-gen-go: simplify generated oneof implementation #708

Closed
@dsnet

Description

@dsnet

The logic to generate support for oneofs are a significant source of complexity:

  • It is complexity to maintain in the generator.
  • It is complexity in the generated code.
  • It is complexity in the proto package as the generated code depends on the following types, functions, variables, and constants: Buffer, SizeVarint, ErrInternalBadWireType, WireBytes, WireEndGroup, WireFixed, WireStartGroup, and WireVarint.
  • It exposes a pseudo-internal method XXX_OneofFuncs.

However, this does not need to be the case:

  • The proto runtime, since switching to the table-driven approach, uses XXX_OneofFuncs in marshal and unmarshal only to obtain type information about the wrappers.

Since the type information is the only information needed, we can accomplish this in a cleaner way. Instead of adding the method, we add an unexported, zero-length field with all the wrapper types:

type FooMessage struct {
    // other fields of FooMessage as usual
    xxx_oneofWrappers [0]struct{
        Oneof_F_Bool
        Oneof_F_Int32
        Oneof_F_Int64
        Oneof_F_Fixed32
        Oneof_F_Fixed64
        Oneof_F_Uint32
        Oneof_F_Uint64
        Oneof_F_Float
        Oneof_F_Double
        Oneof_F_String
        Oneof_F_Bytes
        Oneof_F_Sint32
        Oneof_F_Sint64
        Oneof_F_Enum
        Oneof_F_Message
        Oneof_FGroup
        Oneof_F_Largest_Tag
        Oneof_Value
    }
}

This has the following properties:

  • It removes the need for the exported XXX method (see protoc-gen-go: unexport XXX_ fields from generated types #276). In fact, this approach keeps all of the type information hidden. Using Go reflection, you can obtain a list of all the oneof wrappers types from the type information in the message alone (no need to dynamically call methods).
  • It simplifies the implementation of proto since we don't have to type assert for a method with a relatively complex signature.
  • unsafe.Sizeof(FooMessage{}) remains unchanged
  • The generated code is significantly simpler.

\cc @neild

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions