schema.h 17.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Kenton Varda's avatar
Kenton Varda committed
24 25
#ifndef CAPNP_SCHEMA_H_
#define CAPNP_SCHEMA_H_
26

27
#include <capnp/schema.capnp.h>
28

29
namespace capnp {
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

class Schema;
class StructSchema;
class EnumSchema;
class InterfaceSchema;
class ListSchema;

template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Body::Which Type; };
template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Body::Which Type; };
template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; };
template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; };
template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; };
template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; };

template <typename T>
using SchemaType = typename SchemaType_<T>::Type;
// SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct.

class Schema {
50
  // Convenience wrapper around capnp::schema::Node.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

public:
  inline Schema(): raw(nullptr) {}

  template <typename T>
  static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); }
  // Get the Schema for a particular compiled-in type.

  schema::Node::Reader getProto() const;

  Schema getDependency(uint64_t id) const;
  // Gets the Schema for one of this Schema's dependencies.  For example, if this Schema is for a
  // struct, you could look up the schema for one of its fields' types.  Throws an exception if this
  // schema doesn't actually depend on the given id.  Note that annotation declarations are not
  // considered dependencies for this purpose.

  StructSchema asStruct() const;
  EnumSchema asEnum() const;
  InterfaceSchema asInterface() const;
  // Cast the Schema to a specific type.  Throws an exception if the type doesn't match.

  inline bool operator==(const Schema& other) const { return raw == other.raw; }
  inline bool operator!=(const Schema& other) const { return raw != other.raw; }
  // Determine whether two Schemas are wrapping the exact same underlying data, by identity.  If
  // you want to check if two Schemas represent the same type (but possibly different versions of
  // it), compare their IDs instead.

78
  template <typename T>
79
  void requireUsableAs() const;
80 81 82 83 84 85
  // Throws an exception if a value with this Schema cannot safely be cast to a native value of
  // the given type.  This passes if either:
  // - *this == from<T>()
  // - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and
  //   loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader.

86
private:
87
  const _::RawSchema* raw;
88

89
  inline explicit Schema(const _::RawSchema* raw): raw(raw) {}
90 91

  template <typename T> static inline Schema fromImpl() {
92
    return Schema(&_::rawSchema<T>());
93 94
  }

95
  void requireUsableAs(const _::RawSchema* expected) const;
96

97 98 99
  friend class StructSchema;
  friend class EnumSchema;
  friend class InterfaceSchema;
100 101
  friend class ListSchema;
  friend class SchemaLoader;
102 103 104 105 106 107 108 109 110 111 112 113
};

// -------------------------------------------------------------------

class StructSchema: public Schema {
public:
  StructSchema() = default;

  class Member;
  class Union;
  class MemberList;

114
  MemberList getMembers() const;
Kenton Varda's avatar
Kenton Varda committed
115

116
  kj::Maybe<Member> findMemberByName(kj::StringPtr name) const;
117

118
  Member getMemberByName(kj::StringPtr name) const;
Kenton Varda's avatar
Kenton Varda committed
119 120
  // Like findMemberByName() but throws an exception on failure.

121
private:
122
  StructSchema(const _::RawSchema* raw): Schema(raw) {}
123
  template <typename T> static inline StructSchema fromImpl() {
124
    return StructSchema(&_::rawSchema<T>());
125 126
  }
  friend class Schema;
127 128 129 130
  friend kj::String _::structString(
      _::StructReader reader, const _::RawSchema& schema);
  friend kj::String _::unionString(
      _::StructReader reader, const _::RawSchema& schema, uint memberIndex);
131 132 133 134 135 136 137 138 139 140 141 142
};

class StructSchema::Member {
public:
  Member() = default;

  inline schema::StructNode::Member::Reader getProto() const { return proto; }
  inline StructSchema getContainingStruct() const { return parent; }

  inline uint getIndex() const { return index; }
  // Get the index of this member within the containing struct or union.

143
  kj::Maybe<Union> getContainingUnion() const;
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  // If this a member of a union, gets the containing union schema.

  Union asUnion() const;
  // Cast the member to a Union.  Throws an exception if not a union.

  inline bool operator==(const Member& other) const;
  inline bool operator!=(const Member& other) const { return !(*this == other); }

private:
  StructSchema parent;
  uint unionIndex;  // 0 = none, >0 = actual union index - 1
  uint index;
  mutable schema::StructNode::Member::Reader proto;
  // TODO(soon): Make all reader methods const and then remove this ugly use of "mutable".

  inline Member(StructSchema parent, uint unionIndex, uint index,
                schema::StructNode::Member::Reader proto)
      : parent(parent), unionIndex(unionIndex), index(index), proto(proto) {}

  friend class StructSchema;
};

class StructSchema::Union: public Member {
public:
  Union() = default;

170
  MemberList getMembers() const;
Kenton Varda's avatar
Kenton Varda committed
171

172
  kj::Maybe<Member> findMemberByName(kj::StringPtr name) const;
173

174
  Member getMemberByName(kj::StringPtr name) const;
Kenton Varda's avatar
Kenton Varda committed
175 176
  // Like findMemberByName() but throws an exception on failure.

177 178 179 180 181 182 183 184 185 186 187
private:
  inline Union(const Member& base): Member(base) {}

  friend class StructSchema;
};

class StructSchema::MemberList {
public:
  inline uint size() const { return list.size(); }
  inline Member operator[](uint index) const { return Member(parent, unionIndex, index, list[index]); }

188
  typedef _::IndexingIterator<const MemberList, Member> Iterator;
189 190
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

private:
  StructSchema parent;
  uint unionIndex;
  List<schema::StructNode::Member>::Reader list;

  inline MemberList(StructSchema parent, uint unionIndex,
                    List<schema::StructNode::Member>::Reader list)
      : parent(parent), unionIndex(unionIndex), list(list) {}

  friend class StructSchema;
};

// -------------------------------------------------------------------

class EnumSchema: public Schema {
public:
208 209
  EnumSchema() = default;

210 211 212
  class Enumerant;
  class EnumerantList;

213
  EnumerantList getEnumerants() const;
Kenton Varda's avatar
Kenton Varda committed
214

215
  kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const;
216

217
  Enumerant getEnumerantByName(kj::StringPtr name) const;
Kenton Varda's avatar
Kenton Varda committed
218 219
  // Like findEnumerantByName() but throws an exception on failure.

220
private:
221
  EnumSchema(const _::RawSchema* raw): Schema(raw) {}
222
  template <typename T> static inline EnumSchema fromImpl() {
223
    return EnumSchema(&_::rawSchema<T>());
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
  }
  friend class Schema;
};

class EnumSchema::Enumerant {
public:
  Enumerant() = default;

  inline schema::EnumNode::Enumerant::Reader getProto() const { return proto; }
  inline EnumSchema getContainingEnum() { return parent; }

  inline uint16_t getOrdinal() { return ordinal; }

  inline bool operator==(const Enumerant& other) const;
  inline bool operator!=(const Enumerant& other) const { return !(*this == other); }

private:
  EnumSchema parent;
  uint16_t ordinal;
  mutable schema::EnumNode::Enumerant::Reader proto;
  // TODO(soon): Make all reader methods const and then remove this ugly use of "mutable".

  inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::EnumNode::Enumerant::Reader proto)
      : parent(parent), ordinal(ordinal), proto(proto) {}

  friend class EnumSchema;
};

class EnumSchema::EnumerantList {
public:
  inline uint size() const { return list.size(); }
  inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); }

257
  typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator;
258 259
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

private:
  EnumSchema parent;
  List<schema::EnumNode::Enumerant>::Reader list;

  inline EnumerantList(EnumSchema parent, List<schema::EnumNode::Enumerant>::Reader list)
      : parent(parent), list(list) {}

  friend class EnumSchema;
};

// -------------------------------------------------------------------

class InterfaceSchema: public Schema {
public:
275 276
  InterfaceSchema() = default;

277 278 279
  class Method;
  class MethodList;

280
  MethodList getMethods() const;
Kenton Varda's avatar
Kenton Varda committed
281

282
  kj::Maybe<Method> findMethodByName(kj::StringPtr name) const;
283

284
  Method getMethodByName(kj::StringPtr name) const;
Kenton Varda's avatar
Kenton Varda committed
285 286
  // Like findMethodByName() but throws an exception on failure.

287
private:
288
  InterfaceSchema(const _::RawSchema* raw): Schema(raw) {}
289
  template <typename T> static inline InterfaceSchema fromImpl() {
290
    return InterfaceSchema(&_::rawSchema<T>());
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
  }
  friend class Schema;
};

class InterfaceSchema::Method {
public:
  Method() = default;

  inline schema::InterfaceNode::Method::Reader getProto() const { return proto; }
  inline InterfaceSchema getContainingInterface() { return parent; }

  inline uint16_t getOrdinal() { return ordinal; }

  inline bool operator==(const Method& other) const;
  inline bool operator!=(const Method& other) const { return !(*this == other); }

private:
  InterfaceSchema parent;
  uint16_t ordinal;
  mutable schema::InterfaceNode::Method::Reader proto;
  // TODO(soon): Make all reader methods const and then remove this ugly use of "mutable".

  inline Method(InterfaceSchema parent, uint16_t ordinal,
                schema::InterfaceNode::Method::Reader proto)
      : parent(parent), ordinal(ordinal), proto(proto) {}

  friend class InterfaceSchema;
};

class InterfaceSchema::MethodList {
public:
  inline uint size() const { return list.size(); }
  inline Method operator[](uint index) const { return Method(parent, index, list[index]); }

325
  typedef _::IndexingIterator<const MethodList, Method> Iterator;
326 327
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

private:
  InterfaceSchema parent;
  List<schema::InterfaceNode::Method>::Reader list;

  inline MethodList(InterfaceSchema parent, List<schema::InterfaceNode::Method>::Reader list)
      : parent(parent), list(list) {}

  friend class InterfaceSchema;
};

// -------------------------------------------------------------------

class ListSchema {
  // ListSchema is a little different because list types are not described by schema nodes.  So,
  // ListSchema doesn't subclass Schema.

public:
  ListSchema() = default;

  static ListSchema of(schema::Type::Body::Which primitiveType);
  static ListSchema of(StructSchema elementType);
  static ListSchema of(EnumSchema elementType);
  static ListSchema of(InterfaceSchema elementType);
  static ListSchema of(ListSchema elementType);
  // Construct the schema for a list of the given type.

  static ListSchema of(schema::Type::Reader elementType, Schema context);
  // Construct from an element type schema.  Requires a context which can handle getDependency()
  // requests for any type ID found in the schema.

  inline schema::Type::Body::Which whichElementType() const;
  // Get the element type's "which()".  ListSchema does not actually store a schema::Type::Reader
  // describing the element type, but if it did, this would be equivalent to calling
  // .getBody().which() on that type.

  StructSchema getStructElementType() const;
  EnumSchema getEnumElementType() const;
  InterfaceSchema getInterfaceElementType() const;
  ListSchema getListElementType() const;
  // Get the schema for complex element types.  Each of these throws an exception if the element
  // type is not of the requested kind.

371 372 373
  inline bool operator==(const ListSchema& other) const;
  inline bool operator!=(const ListSchema& other) const { return !(*this == other); }

374
  template <typename T>
375
  void requireUsableAs() const;
376

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
private:
  schema::Type::Body::Which elementType;
  uint8_t nestingDepth;  // 0 for T, 1 for List(T), 2 for List(List(T)), ...
  Schema elementSchema;  // if elementType is struct, enum, interface...

  inline ListSchema(schema::Type::Body::Which elementType)
      : elementType(elementType), nestingDepth(0) {}
  inline ListSchema(schema::Type::Body::Which elementType, Schema elementSchema)
      : elementType(elementType), nestingDepth(0), elementSchema(elementSchema) {}
  inline ListSchema(schema::Type::Body::Which elementType, uint8_t nestingDepth,
                    Schema elementSchema)
      : elementType(elementType), nestingDepth(nestingDepth), elementSchema(elementSchema) {}

  template <typename T>
  struct FromImpl;
  template <typename T> static inline ListSchema fromImpl() {
    return FromImpl<T>::get();
  }

396
  void requireUsableAs(ListSchema expected) const;
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
  friend class Schema;
};

// =======================================================================================
// inline implementation

template <> inline schema::Type::Body::Which Schema::from<Void>() { return schema::Type::Body::VOID_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<bool>() { return schema::Type::Body::BOOL_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<int8_t>() { return schema::Type::Body::INT8_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<int16_t>() { return schema::Type::Body::INT16_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<int32_t>() { return schema::Type::Body::INT32_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<int64_t>() { return schema::Type::Body::INT64_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<uint8_t>() { return schema::Type::Body::UINT8_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<uint16_t>() { return schema::Type::Body::UINT16_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<uint32_t>() { return schema::Type::Body::UINT32_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<uint64_t>() { return schema::Type::Body::UINT64_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<float>() { return schema::Type::Body::FLOAT32_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<double>() { return schema::Type::Body::FLOAT64_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<Text>() { return schema::Type::Body::TEXT_TYPE; }
template <> inline schema::Type::Body::Which Schema::from<Data>() { return schema::Type::Body::DATA_TYPE; }

419
template <typename T>
420
inline void Schema::requireUsableAs() const {
421
  requireUsableAs(&_::rawSchema<T>());
422 423
}

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
inline bool StructSchema::Member::operator==(const Member& other) const {
  return parent == other.parent && unionIndex == other.unionIndex && index == other.index;
}
inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const {
  return parent == other.parent && ordinal == other.ordinal;
}
inline bool InterfaceSchema::Method::operator==(const Method& other) const {
  return parent == other.parent && ordinal == other.ordinal;
}

inline ListSchema ListSchema::of(StructSchema elementType) {
  return ListSchema(schema::Type::Body::STRUCT_TYPE, 0, elementType);
}
inline ListSchema ListSchema::of(EnumSchema elementType) {
  return ListSchema(schema::Type::Body::ENUM_TYPE, 0, elementType);
}
inline ListSchema ListSchema::of(InterfaceSchema elementType) {
  return ListSchema(schema::Type::Body::INTERFACE_TYPE, 0, elementType);
}
inline ListSchema ListSchema::of(ListSchema elementType) {
  return ListSchema(elementType.elementType, elementType.nestingDepth + 1,
                    elementType.elementSchema);
}

inline schema::Type::Body::Which ListSchema::whichElementType() const {
  return nestingDepth == 0 ? elementType : schema::Type::Body::LIST_TYPE;
}

452 453 454 455 456
inline bool ListSchema::operator==(const ListSchema& other) const {
  return elementType == other.elementType && nestingDepth == other.nestingDepth &&
      elementSchema == other.elementSchema;
}

457
template <typename T>
458
inline void ListSchema::requireUsableAs() const {
459 460 461 462 463
  static_assert(kind<T>() == Kind::LIST,
                "ListSchema::requireUsableAs<T>() requires T is a list type.");
  requireUsableAs(Schema::from<T>());
}

464 465 466 467 468
template <typename T>
struct ListSchema::FromImpl<List<T>> {
  static inline ListSchema get() { return of(Schema::from<T>()); }
};

469
}  // namespace capnp
470

Kenton Varda's avatar
Kenton Varda committed
471
#endif  // CAPNP_SCHEMA_H_