capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

schema.h (38332B)


      1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #pragma once
     23 
     24 #if CAPNP_LITE
     25 #error "Reflection APIs, including this header, are not available in lite mode."
     26 #endif
     27 
     28 #undef CONST
     29 // For some ridiculous reason, Windows defines CONST to const. We have an enum value called CONST
     30 // in schema.capnp.h, so if this is defined, compilation is gonna fail. So we undef it because
     31 // that seems strictly better than failing entirely. But this could cause trouble for people later
     32 // on if they, say, include windows.h, then include schema.h, then include another windows API
     33 // header that uses CONST. I suppose they may have to re-#define CONST in between, or change the
     34 // header ordering. Sorry.
     35 //
     36 // Please don't file a bug report telling us to change our enum naming style. You are at least
     37 // seven years too late.
     38 
     39 #include <capnp/schema.capnp.h>
     40 #include <kj/hash.h>
     41 #include <kj/windows-sanity.h>  // work-around macro conflict with `VOID`
     42 
     43 CAPNP_BEGIN_HEADER
     44 
     45 namespace capnp {
     46 
     47 class Schema;
     48 class StructSchema;
     49 class EnumSchema;
     50 class InterfaceSchema;
     51 class ConstSchema;
     52 class ListSchema;
     53 class Type;
     54 
     55 template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
     56 template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; };
     57 template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; };
     58 template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; };
     59 template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; };
     60 template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; };
     61 template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; };
     62 
     63 template <typename T>
     64 using SchemaType = typename SchemaType_<T>::Type;
     65 // SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct.
     66 
     67 namespace _ {  // private
     68 extern const RawSchema NULL_SCHEMA;
     69 extern const RawSchema NULL_STRUCT_SCHEMA;
     70 extern const RawSchema NULL_ENUM_SCHEMA;
     71 extern const RawSchema NULL_INTERFACE_SCHEMA;
     72 extern const RawSchema NULL_CONST_SCHEMA;
     73 // The schema types default to these null (empty) schemas in case of error, especially when
     74 // exceptions are disabled.
     75 }  // namespace _ (private)
     76 
     77 class Schema {
     78   // Convenience wrapper around capnp::schema::Node.
     79 
     80 public:
     81   inline Schema(): raw(&_::NULL_SCHEMA.defaultBrand) {}
     82 
     83   template <typename T>
     84   static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); }
     85   // Get the Schema for a particular compiled-in type.
     86 
     87   schema::Node::Reader getProto() const;
     88   // Get the underlying Cap'n Proto representation of the schema node.  (Note that this accessor
     89   // has performance comparable to accessors of struct-typed fields on Reader classes.)
     90 
     91   kj::ArrayPtr<const word> asUncheckedMessage() const;
     92   // Get the encoded schema node content as a single message segment.  It is safe to read as an
     93   // unchecked message.
     94 
     95   Schema getDependency(uint64_t id) const CAPNP_DEPRECATED("Does not handle generics correctly.");
     96   // DEPRECATED: This method cannot correctly account for generic type parameter bindings that
     97   //   may apply to the dependency. Instead of using this method, use a method of the Schema API
     98   //   that corresponds to the exact kind of dependency. For example, to get a field type, use
     99   //   StructSchema::Field::getType().
    100   //
    101   // Gets the Schema for one of this Schema's dependencies.  For example, if this Schema is for a
    102   // struct, you could look up the schema for one of its fields' types.  Throws an exception if this
    103   // schema doesn't actually depend on the given id.
    104   //
    105   // Note that not all type IDs found in the schema node are considered "dependencies" -- only the
    106   // ones that are needed to implement the dynamic API are.  That includes:
    107   // - Field types.
    108   // - Group types.
    109   // - scopeId for group nodes, but NOT otherwise.
    110   // - Method parameter and return types.
    111   //
    112   // The following are NOT considered dependencies:
    113   // - Nested nodes.
    114   // - scopeId for a non-group node.
    115   // - Annotations.
    116   //
    117   // To obtain schemas for those, you would need a SchemaLoader.
    118 
    119   bool isBranded() const;
    120   // Returns true if this schema represents a non-default parameterization of this type.
    121 
    122   Schema getGeneric() const;
    123   // Get the version of this schema with any brands removed.
    124 
    125   class BrandArgumentList;
    126   BrandArgumentList getBrandArgumentsAtScope(uint64_t scopeId) const;
    127   // Gets the values bound to the brand parameters at the given scope.
    128 
    129   StructSchema asStruct() const;
    130   EnumSchema asEnum() const;
    131   InterfaceSchema asInterface() const;
    132   ConstSchema asConst() const;
    133   // Cast the Schema to a specific type.  Throws an exception if the type doesn't match.  Use
    134   // getProto() to determine type, e.g. getProto().isStruct().
    135 
    136   inline bool operator==(const Schema& other) const { return raw == other.raw; }
    137   inline bool operator!=(const Schema& other) const { return raw != other.raw; }
    138   // Determine whether two Schemas are wrapping the exact same underlying data, by identity.  If
    139   // you want to check if two Schemas represent the same type (but possibly different versions of
    140   // it), compare their IDs instead.
    141 
    142   inline uint hashCode() const { return kj::hashCode(raw); }
    143 
    144   template <typename T>
    145   void requireUsableAs() const;
    146   // Throws an exception if a value with this Schema cannot safely be cast to a native value of
    147   // the given type.  This passes if either:
    148   // - *this == from<T>()
    149   // - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and
    150   //   loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader.
    151 
    152   kj::StringPtr getShortDisplayName() const;
    153   // Get the short version of the node's display name.
    154 
    155   const kj::StringPtr getUnqualifiedName() const;
    156   // Get the display name "nickname" of this node minus the prefix
    157 
    158 private:
    159   const _::RawBrandedSchema* raw;
    160 
    161   inline explicit Schema(const _::RawBrandedSchema* raw): raw(raw) {
    162     KJ_IREQUIRE(raw->lazyInitializer == nullptr,
    163         "Must call ensureInitialized() on RawSchema before constructing Schema.");
    164   }
    165 
    166   template <typename T> static inline Schema fromImpl() {
    167     return Schema(&_::rawSchema<T>());
    168   }
    169 
    170   void requireUsableAs(const _::RawSchema* expected) const;
    171 
    172   uint32_t getSchemaOffset(const schema::Value::Reader& value) const;
    173 
    174   Type getBrandBinding(uint64_t scopeId, uint index) const;
    175   // Look up the binding for a brand parameter used by this Schema. Returns `AnyPointer` if the
    176   // parameter is not bound.
    177   //
    178   // TODO(someday): Public interface for iterating over all bindings?
    179 
    180   Schema getDependency(uint64_t id, uint location) const;
    181   // Look up schema for a particular dependency of this schema. `location` is the dependency
    182   // location number as defined in _::RawBrandedSchema.
    183 
    184   Type interpretType(schema::Type::Reader proto, uint location) const;
    185   // Interpret a schema::Type in the given location within the schema, compiling it into a
    186   // Type object.
    187 
    188   friend class StructSchema;
    189   friend class EnumSchema;
    190   friend class InterfaceSchema;
    191   friend class ConstSchema;
    192   friend class ListSchema;
    193   friend class SchemaLoader;
    194   friend class Type;
    195   friend kj::StringTree _::structString(
    196       _::StructReader reader, const _::RawBrandedSchema& schema);
    197   friend kj::String _::enumString(uint16_t value, const _::RawBrandedSchema& schema);
    198 };
    199 
    200 kj::StringPtr KJ_STRINGIFY(const Schema& schema);
    201 
    202 class Schema::BrandArgumentList {
    203   // A list of generic parameter bindings for parameters of some particular type. Note that since
    204   // parameters on an outer type apply to all inner types as well, a deeply-nested type can have
    205   // multiple BrandArgumentLists that apply to it.
    206   //
    207   // A BrandArgumentList only represents the arguments that the client of the type specified. Since
    208   // new parameters can be added over time, this list may not cover all defined parameters for the
    209   // type. Missing parameters should be treated as AnyPointer. This class's implementation of
    210   // operator[] already does this for you; out-of-bounds access will safely return AnyPointer.
    211 
    212 public:
    213   inline BrandArgumentList(): scopeId(0), size_(0), bindings(nullptr) {}
    214 
    215   inline uint size() const { return size_; }
    216   Type operator[](uint index) const;
    217 
    218   typedef _::IndexingIterator<const BrandArgumentList, Type> Iterator;
    219   inline Iterator begin() const { return Iterator(this, 0); }
    220   inline Iterator end() const { return Iterator(this, size()); }
    221 
    222 private:
    223   uint64_t scopeId;
    224   uint size_;
    225   bool isUnbound;
    226   const _::RawBrandedSchema::Binding* bindings;
    227 
    228   inline BrandArgumentList(uint64_t scopeId, bool isUnbound)
    229       : scopeId(scopeId), size_(0), isUnbound(isUnbound), bindings(nullptr) {}
    230   inline BrandArgumentList(uint64_t scopeId, uint size,
    231                            const _::RawBrandedSchema::Binding* bindings)
    232       : scopeId(scopeId), size_(size), isUnbound(false), bindings(bindings) {}
    233 
    234   friend class Schema;
    235 };
    236 
    237 // -------------------------------------------------------------------
    238 
    239 class StructSchema: public Schema {
    240 public:
    241   inline StructSchema(): Schema(&_::NULL_STRUCT_SCHEMA.defaultBrand) {}
    242 
    243   class Field;
    244   class FieldList;
    245   class FieldSubset;
    246 
    247   FieldList getFields() const;
    248   // List top-level fields of this struct.  This list will contain top-level groups (including
    249   // named unions) but not the members of those groups.  The list does, however, contain the
    250   // members of the unnamed union, if there is one.
    251 
    252   FieldSubset getUnionFields() const;
    253   // If the field contains an unnamed union, get a list of fields in the union, ordered by
    254   // ordinal.  Since discriminant values are assigned sequentially by ordinal, you may index this
    255   // list by discriminant value.
    256 
    257   FieldSubset getNonUnionFields() const;
    258   // Get the fields of this struct which are not in an unnamed union, ordered by ordinal.
    259 
    260   kj::Maybe<Field> findFieldByName(kj::StringPtr name) const;
    261   // Find the field with the given name, or return null if there is no such field.  If the struct
    262   // contains an unnamed union, then this will find fields of that union in addition to fields
    263   // of the outer struct, since they exist in the same namespace.  It will not, however, find
    264   // members of groups (including named unions) -- you must first look up the group itself,
    265   // then dig into its type.
    266 
    267   Field getFieldByName(kj::StringPtr name) const;
    268   // Like findFieldByName() but throws an exception on failure.
    269 
    270   kj::Maybe<Field> getFieldByDiscriminant(uint16_t discriminant) const;
    271   // Finds the field whose `discriminantValue` is equal to the given value, or returns null if
    272   // there is no such field.  (If the schema does not represent a union or a struct containing
    273   // an unnamed union, then this always returns null.)
    274 
    275   bool isStreamResult() const;
    276   // Convenience method to check if this is the result type of a streaming RPC method.
    277 
    278 private:
    279   StructSchema(Schema base): Schema(base) {}
    280   template <typename T> static inline StructSchema fromImpl() {
    281     return StructSchema(Schema(&_::rawBrandedSchema<T>()));
    282   }
    283   friend class Schema;
    284   friend class Type;
    285 };
    286 
    287 class StructSchema::Field {
    288 public:
    289   Field() = default;
    290 
    291   inline schema::Field::Reader getProto() const { return proto; }
    292   inline StructSchema getContainingStruct() const { return parent; }
    293 
    294   inline uint getIndex() const { return index; }
    295   // Get the index of this field within the containing struct or union.
    296 
    297   Type getType() const;
    298   // Get the type of this field. Note that this is preferred over getProto().getType() as this
    299   // method will apply generics.
    300 
    301   uint32_t getDefaultValueSchemaOffset() const;
    302   // For struct, list, and object fields, returns the offset, in words, within the first segment of
    303   // the struct's schema, where this field's default value pointer is located.  The schema is
    304   // always stored as a single-segment unchecked message, which in turn means that the default
    305   // value pointer itself can be treated as the root of an unchecked message -- if you know where
    306   // to find it, which is what this method helps you with.
    307   //
    308   // For blobs, returns the offset of the beginning of the blob's content within the first segment
    309   // of the struct's schema.
    310   //
    311   // This is primarily useful for code generators.  The C++ code generator, for example, embeds
    312   // the entire schema as a raw word array within the generated code.  Of course, to implement
    313   // field accessors, it needs access to those fields' default values.  Embedding separate copies
    314   // of those default values would be redundant since they are already included in the schema, but
    315   // seeking through the schema at runtime to find the default values would be ugly.  Instead,
    316   // the code generator can use getDefaultValueSchemaOffset() to find the offset of the default
    317   // value within the schema, and can simply apply that offset at runtime.
    318   //
    319   // If the above does not make sense, you probably don't need this method.
    320 
    321   inline bool operator==(const Field& other) const;
    322   inline bool operator!=(const Field& other) const { return !(*this == other); }
    323   inline uint hashCode() const;
    324 
    325 private:
    326   StructSchema parent;
    327   uint index;
    328   schema::Field::Reader proto;
    329 
    330   inline Field(StructSchema parent, uint index, schema::Field::Reader proto)
    331       : parent(parent), index(index), proto(proto) {}
    332 
    333   friend class StructSchema;
    334 };
    335 
    336 kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field);
    337 
    338 class StructSchema::FieldList {
    339 public:
    340   FieldList() = default;  // empty list
    341 
    342   inline uint size() const { return list.size(); }
    343   inline Field operator[](uint index) const { return Field(parent, index, list[index]); }
    344 
    345   typedef _::IndexingIterator<const FieldList, Field> Iterator;
    346   inline Iterator begin() const { return Iterator(this, 0); }
    347   inline Iterator end() const { return Iterator(this, size()); }
    348 
    349 private:
    350   StructSchema parent;
    351   List<schema::Field>::Reader list;
    352 
    353   inline FieldList(StructSchema parent, List<schema::Field>::Reader list)
    354       : parent(parent), list(list) {}
    355 
    356   friend class StructSchema;
    357 };
    358 
    359 class StructSchema::FieldSubset {
    360 public:
    361   FieldSubset() = default;  // empty list
    362 
    363   inline uint size() const { return size_; }
    364   inline Field operator[](uint index) const {
    365     return Field(parent, indices[index], list[indices[index]]);
    366   }
    367 
    368   typedef _::IndexingIterator<const FieldSubset, Field> Iterator;
    369   inline Iterator begin() const { return Iterator(this, 0); }
    370   inline Iterator end() const { return Iterator(this, size()); }
    371 
    372 private:
    373   StructSchema parent;
    374   List<schema::Field>::Reader list;
    375   const uint16_t* indices;
    376   uint size_;
    377 
    378   inline FieldSubset(StructSchema parent, List<schema::Field>::Reader list,
    379                      const uint16_t* indices, uint size)
    380       : parent(parent), list(list), indices(indices), size_(size) {}
    381 
    382   friend class StructSchema;
    383 };
    384 
    385 // -------------------------------------------------------------------
    386 
    387 class EnumSchema: public Schema {
    388 public:
    389   inline EnumSchema(): Schema(&_::NULL_ENUM_SCHEMA.defaultBrand) {}
    390 
    391   class Enumerant;
    392   class EnumerantList;
    393 
    394   EnumerantList getEnumerants() const;
    395 
    396   kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const;
    397 
    398   Enumerant getEnumerantByName(kj::StringPtr name) const;
    399   // Like findEnumerantByName() but throws an exception on failure.
    400 
    401 private:
    402   EnumSchema(Schema base): Schema(base) {}
    403   template <typename T> static inline EnumSchema fromImpl() {
    404     return EnumSchema(Schema(&_::rawBrandedSchema<T>()));
    405   }
    406   friend class Schema;
    407   friend class Type;
    408 };
    409 
    410 class EnumSchema::Enumerant {
    411 public:
    412   Enumerant() = default;
    413 
    414   inline schema::Enumerant::Reader getProto() const { return proto; }
    415   inline EnumSchema getContainingEnum() const { return parent; }
    416 
    417   inline uint16_t getOrdinal() const { return ordinal; }
    418   inline uint getIndex() const { return ordinal; }
    419 
    420   inline bool operator==(const Enumerant& other) const;
    421   inline bool operator!=(const Enumerant& other) const { return !(*this == other); }
    422   inline uint hashCode() const;
    423 
    424 private:
    425   EnumSchema parent;
    426   uint16_t ordinal;
    427   schema::Enumerant::Reader proto;
    428 
    429   inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::Enumerant::Reader proto)
    430       : parent(parent), ordinal(ordinal), proto(proto) {}
    431 
    432   friend class EnumSchema;
    433 };
    434 
    435 class EnumSchema::EnumerantList {
    436 public:
    437   EnumerantList() = default;  // empty list
    438 
    439   inline uint size() const { return list.size(); }
    440   inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); }
    441 
    442   typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator;
    443   inline Iterator begin() const { return Iterator(this, 0); }
    444   inline Iterator end() const { return Iterator(this, size()); }
    445 
    446 private:
    447   EnumSchema parent;
    448   List<schema::Enumerant>::Reader list;
    449 
    450   inline EnumerantList(EnumSchema parent, List<schema::Enumerant>::Reader list)
    451       : parent(parent), list(list) {}
    452 
    453   friend class EnumSchema;
    454 };
    455 
    456 // -------------------------------------------------------------------
    457 
    458 class InterfaceSchema: public Schema {
    459 public:
    460   inline InterfaceSchema(): Schema(&_::NULL_INTERFACE_SCHEMA.defaultBrand) {}
    461 
    462   class Method;
    463   class MethodList;
    464 
    465   MethodList getMethods() const;
    466 
    467   kj::Maybe<Method> findMethodByName(kj::StringPtr name) const;
    468 
    469   Method getMethodByName(kj::StringPtr name) const;
    470   // Like findMethodByName() but throws an exception on failure.
    471 
    472   class SuperclassList;
    473 
    474   SuperclassList getSuperclasses() const;
    475   // Get the immediate superclasses of this type, after applying generics.
    476 
    477   bool extends(InterfaceSchema other) const;
    478   // Returns true if `other` is a superclass of this interface (including if `other == *this`).
    479 
    480   kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const;
    481   // Find the superclass of this interface with the given type ID.  Returns null if the interface
    482   // extends no such type.
    483 
    484 private:
    485   InterfaceSchema(Schema base): Schema(base) {}
    486   template <typename T> static inline InterfaceSchema fromImpl() {
    487     return InterfaceSchema(Schema(&_::rawBrandedSchema<T>()));
    488   }
    489   friend class Schema;
    490   friend class Type;
    491 
    492   kj::Maybe<Method> findMethodByName(kj::StringPtr name, uint& counter) const;
    493   bool extends(InterfaceSchema other, uint& counter) const;
    494   kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId, uint& counter) const;
    495   // We protect against malicious schemas with large or cyclic hierarchies by cutting off the
    496   // search when the counter reaches a threshold.
    497 };
    498 
    499 class InterfaceSchema::Method {
    500 public:
    501   Method() = default;
    502 
    503   inline schema::Method::Reader getProto() const { return proto; }
    504   inline InterfaceSchema getContainingInterface() const { return parent; }
    505 
    506   inline uint16_t getOrdinal() const { return ordinal; }
    507   inline uint getIndex() const { return ordinal; }
    508 
    509   bool isStreaming() const { return getResultType().isStreamResult(); }
    510   // Check if this is a streaming method.
    511 
    512   StructSchema getParamType() const;
    513   StructSchema getResultType() const;
    514   // Get the parameter and result types, including substituting generic parameters.
    515 
    516   inline bool operator==(const Method& other) const;
    517   inline bool operator!=(const Method& other) const { return !(*this == other); }
    518   inline uint hashCode() const;
    519 
    520 private:
    521   InterfaceSchema parent;
    522   uint16_t ordinal;
    523   schema::Method::Reader proto;
    524 
    525   inline Method(InterfaceSchema parent, uint16_t ordinal,
    526                 schema::Method::Reader proto)
    527       : parent(parent), ordinal(ordinal), proto(proto) {}
    528 
    529   friend class InterfaceSchema;
    530 };
    531 
    532 class InterfaceSchema::MethodList {
    533 public:
    534   MethodList() = default;  // empty list
    535 
    536   inline uint size() const { return list.size(); }
    537   inline Method operator[](uint index) const { return Method(parent, index, list[index]); }
    538 
    539   typedef _::IndexingIterator<const MethodList, Method> Iterator;
    540   inline Iterator begin() const { return Iterator(this, 0); }
    541   inline Iterator end() const { return Iterator(this, size()); }
    542 
    543 private:
    544   InterfaceSchema parent;
    545   List<schema::Method>::Reader list;
    546 
    547   inline MethodList(InterfaceSchema parent, List<schema::Method>::Reader list)
    548       : parent(parent), list(list) {}
    549 
    550   friend class InterfaceSchema;
    551 };
    552 
    553 class InterfaceSchema::SuperclassList {
    554 public:
    555   SuperclassList() = default;  // empty list
    556 
    557   inline uint size() const { return list.size(); }
    558   InterfaceSchema operator[](uint index) const;
    559 
    560   typedef _::IndexingIterator<const SuperclassList, InterfaceSchema> Iterator;
    561   inline Iterator begin() const { return Iterator(this, 0); }
    562   inline Iterator end() const { return Iterator(this, size()); }
    563 
    564 private:
    565   InterfaceSchema parent;
    566   List<schema::Superclass>::Reader list;
    567 
    568   inline SuperclassList(InterfaceSchema parent, List<schema::Superclass>::Reader list)
    569       : parent(parent), list(list) {}
    570 
    571   friend class InterfaceSchema;
    572 };
    573 
    574 // -------------------------------------------------------------------
    575 
    576 class ConstSchema: public Schema {
    577   // Represents a constant declaration.
    578   //
    579   // `ConstSchema` can be implicitly cast to DynamicValue to read its value.
    580 
    581 public:
    582   inline ConstSchema(): Schema(&_::NULL_CONST_SCHEMA.defaultBrand) {}
    583 
    584   template <typename T>
    585   ReaderFor<T> as() const;
    586   // Read the constant's value.  This is a convenience method equivalent to casting the ConstSchema
    587   // to a DynamicValue and then calling its `as<T>()` method.  For dependency reasons, this method
    588   // is defined in <capnp/dynamic.h>, which you must #include explicitly.
    589 
    590   uint32_t getValueSchemaOffset() const;
    591   // Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer
    592   // type, this gets the offset from the beginning of the constant's schema node to a pointer
    593   // representing the constant value.
    594 
    595   Type getType() const;
    596 
    597 private:
    598   ConstSchema(Schema base): Schema(base) {}
    599   friend class Schema;
    600 };
    601 
    602 // -------------------------------------------------------------------
    603 
    604 class Type {
    605 public:
    606   struct BrandParameter {
    607     uint64_t scopeId;
    608     uint index;
    609   };
    610   struct ImplicitParameter {
    611     uint index;
    612   };
    613 
    614   inline Type();
    615   inline Type(schema::Type::Which primitive);
    616   inline Type(StructSchema schema);
    617   inline Type(EnumSchema schema);
    618   inline Type(InterfaceSchema schema);
    619   inline Type(ListSchema schema);
    620   inline Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind);
    621   inline Type(BrandParameter param);
    622   inline Type(ImplicitParameter param);
    623 
    624   template <typename T>
    625   inline static Type from();
    626   template <typename T>
    627   inline static Type from(T&& value);
    628 
    629   inline schema::Type::Which which() const;
    630 
    631   StructSchema asStruct() const;
    632   EnumSchema asEnum() const;
    633   InterfaceSchema asInterface() const;
    634   ListSchema asList() const;
    635   // Each of these methods may only be called if which() returns the corresponding type.
    636 
    637   kj::Maybe<BrandParameter> getBrandParameter() const;
    638   // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular
    639   // AnyPointer and not a parameter.
    640 
    641   kj::Maybe<ImplicitParameter> getImplicitParameter() const;
    642   // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular
    643   // AnyPointer and not a parameter. "Implicit parameters" refer to type parameters on methods.
    644 
    645   inline schema::Type::AnyPointer::Unconstrained::Which whichAnyPointerKind() const;
    646   // Only callable if which() returns ANY_POINTER.
    647 
    648   inline bool isVoid() const;
    649   inline bool isBool() const;
    650   inline bool isInt8() const;
    651   inline bool isInt16() const;
    652   inline bool isInt32() const;
    653   inline bool isInt64() const;
    654   inline bool isUInt8() const;
    655   inline bool isUInt16() const;
    656   inline bool isUInt32() const;
    657   inline bool isUInt64() const;
    658   inline bool isFloat32() const;
    659   inline bool isFloat64() const;
    660   inline bool isText() const;
    661   inline bool isData() const;
    662   inline bool isList() const;
    663   inline bool isEnum() const;
    664   inline bool isStruct() const;
    665   inline bool isInterface() const;
    666   inline bool isAnyPointer() const;
    667 
    668   bool operator==(const Type& other) const;
    669   inline bool operator!=(const Type& other) const { return !(*this == other); }
    670 
    671   uint hashCode() const;
    672 
    673   inline Type wrapInList(uint depth = 1) const;
    674   // Return the Type formed by wrapping this type in List() `depth` times.
    675 
    676   inline Type(schema::Type::Which derived, const _::RawBrandedSchema* schema);
    677   // For internal use.
    678 
    679 private:
    680   schema::Type::Which baseType;  // type not including applications of List()
    681   uint8_t listDepth;             // 0 for T, 1 for List(T), 2 for List(List(T)), ...
    682 
    683   bool isImplicitParam;
    684   // If true, this refers to an implicit method parameter. baseType must be ANY_POINTER, scopeId
    685   // must be zero, and paramIndex indicates the parameter index.
    686 
    687   union {
    688     uint16_t paramIndex;
    689     // If baseType is ANY_POINTER but this Type actually refers to a type parameter, this is the
    690     // index of the parameter among the parameters at its scope, and `scopeId` below is the type ID
    691     // of the scope where the parameter was defined.
    692 
    693     schema::Type::AnyPointer::Unconstrained::Which anyPointerKind;
    694     // If scopeId is zero and isImplicitParam is false.
    695   };
    696 
    697   union {
    698     const _::RawBrandedSchema* schema;  // if type is struct, enum, interface...
    699     uint64_t scopeId;  // if type is AnyPointer but it's actually a type parameter...
    700   };
    701 
    702   Type(schema::Type::Which baseType, uint8_t listDepth, const _::RawBrandedSchema* schema)
    703       : baseType(baseType), listDepth(listDepth), schema(schema) {
    704     KJ_IREQUIRE(baseType != schema::Type::ANY_POINTER);
    705   }
    706 
    707   void requireUsableAs(Type expected) const;
    708 
    709   template <typename T, Kind k>
    710   struct FromValueImpl;
    711 
    712   friend class ListSchema;  // only for requireUsableAs()
    713 };
    714 
    715 // -------------------------------------------------------------------
    716 
    717 class ListSchema {
    718   // ListSchema is a little different because list types are not described by schema nodes.  So,
    719   // ListSchema doesn't subclass Schema.
    720 
    721 public:
    722   ListSchema() = default;
    723 
    724   static ListSchema of(schema::Type::Which primitiveType);
    725   static ListSchema of(StructSchema elementType);
    726   static ListSchema of(EnumSchema elementType);
    727   static ListSchema of(InterfaceSchema elementType);
    728   static ListSchema of(ListSchema elementType);
    729   static ListSchema of(Type elementType);
    730   // Construct the schema for a list of the given type.
    731 
    732   static ListSchema of(schema::Type::Reader elementType, Schema context)
    733       CAPNP_DEPRECATED("Does not handle generics correctly.");
    734   // DEPRECATED: This method cannot correctly account for generic type parameter bindings that
    735   //   may apply to the input type. Instead of using this method, use a method of the Schema API
    736   //   that corresponds to the exact kind of dependency. For example, to get a field type, use
    737   //   StructSchema::Field::getType().
    738   //
    739   // Construct from an element type schema.  Requires a context which can handle getDependency()
    740   // requests for any type ID found in the schema.
    741 
    742   Type getElementType() const;
    743 
    744   inline schema::Type::Which whichElementType() const;
    745   // Get the element type's "which()".  ListSchema does not actually store a schema::Type::Reader
    746   // describing the element type, but if it did, this would be equivalent to calling
    747   // .getBody().which() on that type.
    748 
    749   StructSchema getStructElementType() const;
    750   EnumSchema getEnumElementType() const;
    751   InterfaceSchema getInterfaceElementType() const;
    752   ListSchema getListElementType() const;
    753   // Get the schema for complex element types.  Each of these throws an exception if the element
    754   // type is not of the requested kind.
    755 
    756   inline bool operator==(const ListSchema& other) const { return elementType == other.elementType; }
    757   inline bool operator!=(const ListSchema& other) const { return elementType != other.elementType; }
    758 
    759   template <typename T>
    760   void requireUsableAs() const;
    761 
    762 private:
    763   Type elementType;
    764 
    765   inline explicit ListSchema(Type elementType): elementType(elementType) {}
    766 
    767   template <typename T>
    768   struct FromImpl;
    769   template <typename T> static inline ListSchema fromImpl() {
    770     return FromImpl<T>::get();
    771   }
    772 
    773   void requireUsableAs(ListSchema expected) const;
    774 
    775   friend class Schema;
    776 };
    777 
    778 // =======================================================================================
    779 // inline implementation
    780 
    781 template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; }
    782 template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; }
    783 template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; }
    784 template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; }
    785 template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; }
    786 template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; }
    787 template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; }
    788 template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; }
    789 template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; }
    790 template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; }
    791 template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; }
    792 template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; }
    793 template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; }
    794 template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; }
    795 
    796 inline Schema Schema::getDependency(uint64_t id) const {
    797   return getDependency(id, 0);
    798 }
    799 
    800 inline bool Schema::isBranded() const {
    801   return raw != &raw->generic->defaultBrand;
    802 }
    803 
    804 inline Schema Schema::getGeneric() const {
    805   return Schema(&raw->generic->defaultBrand);
    806 }
    807 
    808 template <typename T>
    809 inline void Schema::requireUsableAs() const {
    810   requireUsableAs(&_::rawSchema<T>());
    811 }
    812 
    813 inline bool StructSchema::Field::operator==(const Field& other) const {
    814   return parent == other.parent && index == other.index;
    815 }
    816 inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const {
    817   return parent == other.parent && ordinal == other.ordinal;
    818 }
    819 inline bool InterfaceSchema::Method::operator==(const Method& other) const {
    820   return parent == other.parent && ordinal == other.ordinal;
    821 }
    822 
    823 inline uint StructSchema::Field::hashCode() const {
    824   return kj::hashCode(parent, index);
    825 }
    826 inline uint EnumSchema::Enumerant::hashCode() const {
    827   return kj::hashCode(parent, ordinal);
    828 }
    829 inline uint InterfaceSchema::Method::hashCode() const {
    830   return kj::hashCode(parent, ordinal);
    831 }
    832 
    833 inline ListSchema ListSchema::of(StructSchema elementType) {
    834   return ListSchema(Type(elementType));
    835 }
    836 inline ListSchema ListSchema::of(EnumSchema elementType) {
    837   return ListSchema(Type(elementType));
    838 }
    839 inline ListSchema ListSchema::of(InterfaceSchema elementType) {
    840   return ListSchema(Type(elementType));
    841 }
    842 inline ListSchema ListSchema::of(ListSchema elementType) {
    843   return ListSchema(Type(elementType));
    844 }
    845 inline ListSchema ListSchema::of(Type elementType) {
    846   return ListSchema(elementType);
    847 }
    848 
    849 inline Type ListSchema::getElementType() const {
    850   return elementType;
    851 }
    852 
    853 inline schema::Type::Which ListSchema::whichElementType() const {
    854   return elementType.which();
    855 }
    856 
    857 inline StructSchema ListSchema::getStructElementType() const {
    858   return elementType.asStruct();
    859 }
    860 
    861 inline EnumSchema ListSchema::getEnumElementType() const {
    862   return elementType.asEnum();
    863 }
    864 
    865 inline InterfaceSchema ListSchema::getInterfaceElementType() const {
    866   return elementType.asInterface();
    867 }
    868 
    869 inline ListSchema ListSchema::getListElementType() const {
    870   return elementType.asList();
    871 }
    872 
    873 template <typename T>
    874 inline void ListSchema::requireUsableAs() const {
    875   static_assert(kind<T>() == Kind::LIST,
    876                 "ListSchema::requireUsableAs<T>() requires T is a list type.");
    877   requireUsableAs(Schema::from<T>());
    878 }
    879 
    880 inline void ListSchema::requireUsableAs(ListSchema expected) const {
    881   elementType.requireUsableAs(expected.elementType);
    882 }
    883 
    884 template <typename T>
    885 struct ListSchema::FromImpl<List<T>> {
    886   static inline ListSchema get() { return of(Schema::from<T>()); }
    887 };
    888 
    889 inline Type::Type(): baseType(schema::Type::VOID), listDepth(0), schema(nullptr) {}
    890 inline Type::Type(schema::Type::Which primitive)
    891     : baseType(primitive), listDepth(0), isImplicitParam(false) {
    892   KJ_IREQUIRE(primitive != schema::Type::STRUCT &&
    893               primitive != schema::Type::ENUM &&
    894               primitive != schema::Type::INTERFACE &&
    895               primitive != schema::Type::LIST);
    896   if (primitive == schema::Type::ANY_POINTER) {
    897     scopeId = 0;
    898     anyPointerKind = schema::Type::AnyPointer::Unconstrained::ANY_KIND;
    899   } else {
    900     schema = nullptr;
    901   }
    902 }
    903 inline Type::Type(schema::Type::Which derived, const _::RawBrandedSchema* schema)
    904     : baseType(derived), listDepth(0), isImplicitParam(false), schema(schema) {
    905   KJ_IREQUIRE(derived == schema::Type::STRUCT ||
    906               derived == schema::Type::ENUM ||
    907               derived == schema::Type::INTERFACE);
    908 }
    909 
    910 inline Type::Type(StructSchema schema)
    911     : baseType(schema::Type::STRUCT), listDepth(0), schema(schema.raw) {}
    912 inline Type::Type(EnumSchema schema)
    913     : baseType(schema::Type::ENUM), listDepth(0), schema(schema.raw) {}
    914 inline Type::Type(InterfaceSchema schema)
    915     : baseType(schema::Type::INTERFACE), listDepth(0), schema(schema.raw) {}
    916 inline Type::Type(ListSchema schema)
    917     : Type(schema.getElementType()) { ++listDepth; }
    918 inline Type::Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind)
    919     : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false),
    920       anyPointerKind(anyPointerKind), scopeId(0) {}
    921 inline Type::Type(BrandParameter param)
    922     : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false),
    923       paramIndex(param.index), scopeId(param.scopeId) {}
    924 inline Type::Type(ImplicitParameter param)
    925     : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(true),
    926       paramIndex(param.index), scopeId(0) {}
    927 
    928 inline schema::Type::Which Type::which() const {
    929   return listDepth > 0 ? schema::Type::LIST : baseType;
    930 }
    931 
    932 inline schema::Type::AnyPointer::Unconstrained::Which Type::whichAnyPointerKind() const {
    933   KJ_IREQUIRE(baseType == schema::Type::ANY_POINTER);
    934   return !isImplicitParam && scopeId == 0 ? anyPointerKind
    935       : schema::Type::AnyPointer::Unconstrained::ANY_KIND;
    936 }
    937 
    938 template <typename T>
    939 inline Type Type::from() { return Type(Schema::from<T>()); }
    940 
    941 template <typename T, Kind k>
    942 struct Type::FromValueImpl {
    943   template <typename U>
    944   static inline Type type(U&& value) {
    945     return Type::from<T>();
    946   }
    947 };
    948 
    949 template <typename T>
    950 struct Type::FromValueImpl<T, Kind::OTHER> {
    951   template <typename U>
    952   static inline Type type(U&& value) {
    953     // All dynamic types have getSchema().
    954     return value.getSchema();
    955   }
    956 };
    957 
    958 template <typename T>
    959 inline Type Type::from(T&& value) {
    960   typedef FromAny<kj::Decay<T>> Base;
    961   return Type::FromValueImpl<Base, kind<Base>()>::type(kj::fwd<T>(value));
    962 }
    963 
    964 inline bool Type::isVoid   () const { return baseType == schema::Type::VOID     && listDepth == 0; }
    965 inline bool Type::isBool   () const { return baseType == schema::Type::BOOL     && listDepth == 0; }
    966 inline bool Type::isInt8   () const { return baseType == schema::Type::INT8     && listDepth == 0; }
    967 inline bool Type::isInt16  () const { return baseType == schema::Type::INT16    && listDepth == 0; }
    968 inline bool Type::isInt32  () const { return baseType == schema::Type::INT32    && listDepth == 0; }
    969 inline bool Type::isInt64  () const { return baseType == schema::Type::INT64    && listDepth == 0; }
    970 inline bool Type::isUInt8  () const { return baseType == schema::Type::UINT8    && listDepth == 0; }
    971 inline bool Type::isUInt16 () const { return baseType == schema::Type::UINT16   && listDepth == 0; }
    972 inline bool Type::isUInt32 () const { return baseType == schema::Type::UINT32   && listDepth == 0; }
    973 inline bool Type::isUInt64 () const { return baseType == schema::Type::UINT64   && listDepth == 0; }
    974 inline bool Type::isFloat32() const { return baseType == schema::Type::FLOAT32  && listDepth == 0; }
    975 inline bool Type::isFloat64() const { return baseType == schema::Type::FLOAT64  && listDepth == 0; }
    976 inline bool Type::isText   () const { return baseType == schema::Type::TEXT     && listDepth == 0; }
    977 inline bool Type::isData   () const { return baseType == schema::Type::DATA     && listDepth == 0; }
    978 inline bool Type::isList   () const { return listDepth > 0; }
    979 inline bool Type::isEnum   () const { return baseType == schema::Type::ENUM     && listDepth == 0; }
    980 inline bool Type::isStruct () const { return baseType == schema::Type::STRUCT   && listDepth == 0; }
    981 inline bool Type::isInterface() const {
    982   return baseType == schema::Type::INTERFACE && listDepth == 0;
    983 }
    984 inline bool Type::isAnyPointer() const {
    985   return baseType == schema::Type::ANY_POINTER && listDepth == 0;
    986 }
    987 
    988 inline Type Type::wrapInList(uint depth) const {
    989   Type result = *this;
    990   result.listDepth += depth;
    991   return result;
    992 }
    993 
    994 }  // namespace capnp
    995 
    996 CAPNP_END_HEADER