Search code examples
capnproto

Examples of using unique IDs in Cap'n Proto


Cap'n Proto documentation contains a single sentence on the rationale behind Unique ID:

IDs exist to provide a relatively short yet unambiguous way to refer to a type or annotation from another context.

I wasn't able to find any examples of this kind of referring. Can anybody point me to them? Thanks.


Solution

  • One example would be Cap'n Proto's own RPC protocol. An RPC receiver can potentially implement any arbitrary set of interface. So, the caller specifies which method they wish to call by sending the interface's type ID plus the method number. See Call.interfaceId in rpc.capnp.

    Anything that looks at annotations also tends to need to use IDs, as annotations in the compiled schema are identified only by their ID. Code which inspects annotations will therefore need to do so by ID. See, for example, this helper function and this call site in Cap'n Proto's own C++ code generator, which needs to honor the annotation that sets the C++ namespace for the file.

    With all that said, in general, you should be wary of using type IDs. Using type IDs in a protocol is sort of like relying on dynamic_cast in C++ -- it's not always bad, but it hints at a poor design that isn't as type-safe as it could be. For example, if you have a message that could contain one of N different types, you might be tempted to define it like:

    # BAD DESIGN
    struct MyApplicationMessage {
      typeId @0 :UInt64;
      value @1 :AnyStruct;
    }
    

    Usually, what you really want here is a union that contains exactly the set of types expected for this message. If you use a union, then type IDs don't matter (and you save space on the wire, since a union tag is only 2 bytes rather than 8).