// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // This file defines classes that can be used to manipulate messages based on schemas that are not // known until runtime. This is also useful for writing generic code that uses schemas to handle // arbitrary types in a generic way. // // Each of the classes defined here has a to() template method which converts an instance back to a // native type. This method will throw an exception if the requested type does not match the // schema. To convert native types to dynamic, use DynamicFactory. // // As always, underlying data is validated lazily, so you have to actually traverse the whole // message if you want to validate all content. #ifndef CAPNP_DYNAMIC_H_ #define CAPNP_DYNAMIC_H_ #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) #pragma GCC system_header #endif #include "schema.h" #include "layout.h" #include "message.h" #include "any.h" #include "capability.h" namespace capnp { class MessageReader; class MessageBuilder; struct DynamicValue { DynamicValue() = delete; enum Type { UNKNOWN, // Means that the value has unknown type and content because it comes from a newer version of // the schema, or from a newer version of Cap'n Proto that has new features that this version // doesn't understand. VOID, BOOL, INT, UINT, FLOAT, TEXT, DATA, LIST, ENUM, STRUCT, CAPABILITY, ANY_POINTER }; class Reader; class Builder; class Pipeline; }; class DynamicEnum; struct DynamicStruct { DynamicStruct() = delete; class Reader; class Builder; class Pipeline; }; struct DynamicList { DynamicList() = delete; class Reader; class Builder; }; struct DynamicCapability { DynamicCapability() = delete; class Client; class Server; }; template <> class Orphan<DynamicValue>; template <Kind k> struct DynamicTypeFor_; template <> struct DynamicTypeFor_<Kind::ENUM> { typedef DynamicEnum Type; }; template <> struct DynamicTypeFor_<Kind::STRUCT> { typedef DynamicStruct Type; }; template <> struct DynamicTypeFor_<Kind::LIST> { typedef DynamicList Type; }; template <> struct DynamicTypeFor_<Kind::INTERFACE> { typedef DynamicCapability Type; }; template <typename T> using DynamicTypeFor = typename DynamicTypeFor_<kind<T>()>::Type; template <typename T> ReaderFor<DynamicTypeFor<FromReader<T>>> toDynamic(T&& value); template <typename T> BuilderFor<DynamicTypeFor<FromBuilder<T>>> toDynamic(T&& value); template <typename T> DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value); template <typename T> typename DynamicTypeFor<FromServer<T>>::Client toDynamic(kj::Own<T>&& value); namespace _ { // private template <> struct Kind_<DynamicValue > { static constexpr Kind kind = Kind::OTHER; }; template <> struct Kind_<DynamicEnum > { static constexpr Kind kind = Kind::OTHER; }; template <> struct Kind_<DynamicStruct > { static constexpr Kind kind = Kind::OTHER; }; template <> struct Kind_<DynamicList > { static constexpr Kind kind = Kind::OTHER; }; template <> struct Kind_<DynamicCapability> { static constexpr Kind kind = Kind::OTHER; }; } // namespace _ (private) template <> inline constexpr Style style<DynamicValue >() { return Style::POINTER; } template <> inline constexpr Style style<DynamicEnum >() { return Style::PRIMITIVE; } template <> inline constexpr Style style<DynamicStruct >() { return Style::STRUCT; } template <> inline constexpr Style style<DynamicList >() { return Style::POINTER; } template <> inline constexpr Style style<DynamicCapability>() { return Style::CAPABILITY; } // ------------------------------------------------------------------- class DynamicEnum { public: DynamicEnum() = default; inline DynamicEnum(EnumSchema::Enumerant enumerant) : schema(enumerant.getContainingEnum()), value(enumerant.getOrdinal()) {} inline DynamicEnum(EnumSchema schema, uint16_t value) : schema(schema), value(value) {} template <typename T, typename = kj::EnableIf<kind<T>() == Kind::ENUM>> inline DynamicEnum(T&& value): DynamicEnum(toDynamic(value)) {} template <typename T> inline T as() const { return static_cast<T>(asImpl(typeId<T>())); } // Cast to a native enum type. inline EnumSchema getSchema() const { return schema; } kj::Maybe<EnumSchema::Enumerant> getEnumerant() const; // Get which enumerant this enum value represents. Returns nullptr if the numeric value does not // correspond to any enumerant in the schema -- this can happen if the data was built using a // newer schema that has more values defined. inline uint16_t getRaw() const { return value; } // Returns the raw underlying enum value. private: EnumSchema schema; uint16_t value; uint16_t asImpl(uint64_t requestedTypeId) const; friend struct DynamicStruct; friend struct DynamicList; friend struct DynamicValue; template <typename T> friend DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value); }; // ------------------------------------------------------------------- class DynamicStruct::Reader { public: typedef DynamicStruct Reads; Reader() = default; template <typename T, typename = kj::EnableIf<kind<FromReader<T>>() == Kind::STRUCT>> inline Reader(T&& value): Reader(toDynamic(value)) {} inline MessageSize totalSize() const { return reader.totalSize().asPublic(); } template <typename T> typename T::Reader as() const; // Convert the dynamic struct to its compiled-in type. inline StructSchema getSchema() const { return schema; } DynamicValue::Reader get(StructSchema::Field field) const; // Read the given field value. bool has(StructSchema::Field field) const; // Tests whether the given field is set to its default value. For pointer values, this does // not actually traverse the value comparing it with the default, but simply returns true if the // pointer is non-null. For members of unions, has() returns false if the union member is not // active, but does not necessarily return true if the member is active (depends on the field's // value). kj::Maybe<StructSchema::Field> which() const; // If the struct contains an (unnamed) union, and the currently-active field within that union // is known, this returns that field. Otherwise, it returns null. In other words, this returns // null if there is no union present _or_ if the union's discriminant is set to an unrecognized // value. This could happen in particular when receiving a message from a sender who has a // newer version of the protocol and is using a field of the union that you don't know about yet. DynamicValue::Reader get(kj::StringPtr name) const; bool has(kj::StringPtr name) const; // Shortcuts to access fields by name. These throw exceptions if no such field exists. private: StructSchema schema; _::StructReader reader; inline Reader(StructSchema schema, _::StructReader reader) : schema(schema), reader(reader) {} Reader(StructSchema schema, const _::OrphanBuilder& orphan); bool isSetInUnion(StructSchema::Field field) const; void verifySetInUnion(StructSchema::Field field) const; static DynamicValue::Reader getImpl(_::StructReader reader, StructSchema::Field field); template <typename T, Kind K> friend struct _::PointerHelpers; friend class DynamicStruct::Builder; friend struct DynamicList; friend class MessageReader; friend class MessageBuilder; template <typename T, ::capnp::Kind k> friend struct ::capnp::ToDynamic_; friend kj::StringTree _::structString( _::StructReader reader, const _::RawBrandedSchema& schema); friend class Orphanage; friend class Orphan<DynamicStruct>; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; }; class DynamicStruct::Builder { public: typedef DynamicStruct Builds; Builder() = default; inline Builder(decltype(nullptr)) {} template <typename T, typename = kj::EnableIf<kind<FromBuilder<T>>() == Kind::STRUCT>> inline Builder(T&& value): Builder(toDynamic(value)) {} inline MessageSize totalSize() const { return asReader().totalSize(); } template <typename T> typename T::Builder as(); // Cast to a particular struct type. inline StructSchema getSchema() const { return schema; } DynamicValue::Builder get(StructSchema::Field field); // Read the given field value. inline bool has(StructSchema::Field field) { return asReader().has(field); } // Tests whether the given field is set to its default value. For pointer values, this does // not actually traverse the value comparing it with the default, but simply returns true if the // pointer is non-null. For members of unions, has() returns whether the field is currently // active and the union as a whole is non-default -- so, the only time has() will return false // for an active union field is if it is the default active field and it has its default value. kj::Maybe<StructSchema::Field> which(); // If the struct contains an (unnamed) union, and the currently-active field within that union // is known, this returns that field. Otherwise, it returns null. In other words, this returns // null if there is no union present _or_ if the union's discriminant is set to an unrecognized // value. This could happen in particular when receiving a message from a sender who has a // newer version of the protocol and is using a field of the union that you don't know about yet. void set(StructSchema::Field field, const DynamicValue::Reader& value); // Set the given field value. DynamicValue::Builder init(StructSchema::Field field); DynamicValue::Builder init(StructSchema::Field field, uint size); // Init a struct, list, or blob field. void adopt(StructSchema::Field field, Orphan<DynamicValue>&& orphan); Orphan<DynamicValue> disown(StructSchema::Field field); // Adopt/disown. This works even for non-pointer fields: adopt() becomes equivalent to set() // and disown() becomes like get() followed by clear(). void clear(StructSchema::Field field); // Clear a field, setting it to its default value. For pointer fields, this actually makes the // field null. DynamicValue::Builder get(kj::StringPtr name); bool has(kj::StringPtr name); void set(kj::StringPtr name, const DynamicValue::Reader& value); void set(kj::StringPtr name, std::initializer_list<DynamicValue::Reader> value); DynamicValue::Builder init(kj::StringPtr name); DynamicValue::Builder init(kj::StringPtr name, uint size); void adopt(kj::StringPtr name, Orphan<DynamicValue>&& orphan); Orphan<DynamicValue> disown(kj::StringPtr name); void clear(kj::StringPtr name); // Shortcuts to access fields by name. These throw exceptions if no such field exists. Reader asReader() const; private: StructSchema schema; _::StructBuilder builder; inline Builder(StructSchema schema, _::StructBuilder builder) : schema(schema), builder(builder) {} Builder(StructSchema schema, _::OrphanBuilder& orphan); bool isSetInUnion(StructSchema::Field field); void verifySetInUnion(StructSchema::Field field); void setInUnion(StructSchema::Field field); template <typename T, Kind k> friend struct _::PointerHelpers; friend struct DynamicList; friend class MessageReader; friend class MessageBuilder; template <typename T, ::capnp::Kind k> friend struct ::capnp::ToDynamic_; friend class Orphanage; friend class Orphan<DynamicStruct>; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; }; class DynamicStruct::Pipeline { public: typedef DynamicStruct Pipelines; inline Pipeline(decltype(nullptr)): typeless(nullptr) {} template <typename T> typename T::Pipeline releaseAs(); // Convert the dynamic pipeline to its compiled-in type. inline StructSchema getSchema() { return schema; } DynamicValue::Pipeline get(StructSchema::Field field); // Read the given field value. DynamicValue::Pipeline get(kj::StringPtr name); // Get by string name. private: StructSchema schema; AnyPointer::Pipeline typeless; inline explicit Pipeline(StructSchema schema, AnyPointer::Pipeline&& typeless) : schema(schema), typeless(kj::mv(typeless)) {} friend class Request<DynamicStruct, DynamicStruct>; }; // ------------------------------------------------------------------- class DynamicList::Reader { public: typedef DynamicList Reads; inline Reader(): reader(ElementSize::VOID) {} template <typename T, typename = kj::EnableIf<kind<FromReader<T>>() == Kind::LIST>> inline Reader(T&& value): Reader(toDynamic(value)) {} template <typename T> typename T::Reader as() const; // Try to convert to any List<T>, Data, or Text. Throws an exception if the underlying data // can't possibly represent the requested type. inline ListSchema getSchema() const { return schema; } inline uint size() const { return unbound(reader.size() / ELEMENTS); } DynamicValue::Reader operator[](uint index) const; typedef _::IndexingIterator<const Reader, DynamicValue::Reader> Iterator; inline Iterator begin() const { return Iterator(this, 0); } inline Iterator end() const { return Iterator(this, size()); } private: ListSchema schema; _::ListReader reader; Reader(ListSchema schema, _::ListReader reader): schema(schema), reader(reader) {} Reader(ListSchema schema, const _::OrphanBuilder& orphan); template <typename T, Kind k> friend struct _::PointerHelpers; friend struct DynamicStruct; friend class DynamicList::Builder; template <typename T, ::capnp::Kind k> friend struct ::capnp::ToDynamic_; friend class Orphanage; friend class Orphan<DynamicList>; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; }; class DynamicList::Builder { public: typedef DynamicList Builds; inline Builder(): builder(ElementSize::VOID) {} inline Builder(decltype(nullptr)): builder(ElementSize::VOID) {} template <typename T, typename = kj::EnableIf<kind<FromBuilder<T>>() == Kind::LIST>> inline Builder(T&& value): Builder(toDynamic(value)) {} template <typename T> typename T::Builder as(); // Try to convert to any List<T>, Data, or Text. Throws an exception if the underlying data // can't possibly represent the requested type. inline ListSchema getSchema() const { return schema; } inline uint size() const { return unbound(builder.size() / ELEMENTS); } DynamicValue::Builder operator[](uint index); void set(uint index, const DynamicValue::Reader& value); DynamicValue::Builder init(uint index, uint size); void adopt(uint index, Orphan<DynamicValue>&& orphan); Orphan<DynamicValue> disown(uint index); typedef _::IndexingIterator<Builder, DynamicStruct::Builder> Iterator; inline Iterator begin() { return Iterator(this, 0); } inline Iterator end() { return Iterator(this, size()); } void copyFrom(std::initializer_list<DynamicValue::Reader> value); Reader asReader() const; private: ListSchema schema; _::ListBuilder builder; Builder(ListSchema schema, _::ListBuilder builder): schema(schema), builder(builder) {} Builder(ListSchema schema, _::OrphanBuilder& orphan); template <typename T, Kind k> friend struct _::PointerHelpers; friend struct DynamicStruct; template <typename T, ::capnp::Kind k> friend struct ::capnp::ToDynamic_; friend class Orphanage; template <typename T, Kind k> friend struct _::OrphanGetImpl; friend class Orphan<DynamicList>; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; }; // ------------------------------------------------------------------- class DynamicCapability::Client: public Capability::Client { public: typedef DynamicCapability Calls; typedef DynamicCapability Reads; Client() = default; template <typename T, typename = kj::EnableIf<kind<FromClient<T>>() == Kind::INTERFACE>> inline Client(T&& client); template <typename T, typename = kj::EnableIf<kj::canConvert<T*, DynamicCapability::Server*>()>> inline Client(kj::Own<T>&& server); template <typename T, typename = kj::EnableIf<kind<T>() == Kind::INTERFACE>> typename T::Client as(); template <typename T, typename = kj::EnableIf<kind<T>() == Kind::INTERFACE>> typename T::Client releaseAs(); // Convert to any client type. Client upcast(InterfaceSchema requestedSchema); // Upcast to a superclass. Throws an exception if `schema` is not a superclass. inline InterfaceSchema getSchema() { return schema; } Request<DynamicStruct, DynamicStruct> newRequest( InterfaceSchema::Method method, kj::Maybe<MessageSize> sizeHint = nullptr); Request<DynamicStruct, DynamicStruct> newRequest( kj::StringPtr methodName, kj::Maybe<MessageSize> sizeHint = nullptr); private: InterfaceSchema schema; Client(InterfaceSchema schema, kj::Own<ClientHook>&& hook) : Capability::Client(kj::mv(hook)), schema(schema) {} template <typename T> inline Client(InterfaceSchema schema, kj::Own<T>&& server); friend struct Capability; friend struct DynamicStruct; friend struct DynamicList; friend struct DynamicValue; friend class Orphan<DynamicCapability>; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; template <typename T, Kind k> friend struct _::PointerHelpers; }; class DynamicCapability::Server: public Capability::Server { public: typedef DynamicCapability Serves; Server(InterfaceSchema schema): schema(schema) {} virtual kj::Promise<void> call(InterfaceSchema::Method method, CallContext<DynamicStruct, DynamicStruct> context) = 0; kj::Promise<void> dispatchCall(uint64_t interfaceId, uint16_t methodId, CallContext<AnyPointer, AnyPointer> context) override final; inline InterfaceSchema getSchema() const { return schema; } private: InterfaceSchema schema; }; template <> class Request<DynamicStruct, DynamicStruct>: public DynamicStruct::Builder { // Specialization of `Request<T, U>` for DynamicStruct. public: inline Request(DynamicStruct::Builder builder, kj::Own<RequestHook>&& hook, StructSchema resultSchema) : DynamicStruct::Builder(builder), hook(kj::mv(hook)), resultSchema(resultSchema) {} RemotePromise<DynamicStruct> send(); // Send the call and return a promise for the results. private: kj::Own<RequestHook> hook; StructSchema resultSchema; friend class Capability::Client; friend struct DynamicCapability; template <typename, typename> friend class CallContext; friend class RequestHook; }; template <> class CallContext<DynamicStruct, DynamicStruct>: public kj::DisallowConstCopy { // Wrapper around CallContextHook with a specific return type. // // Methods of this class may only be called from within the server's event loop, not from other // threads. public: explicit CallContext(CallContextHook& hook, StructSchema paramType, StructSchema resultType); DynamicStruct::Reader getParams(); void releaseParams(); DynamicStruct::Builder getResults(kj::Maybe<MessageSize> sizeHint = nullptr); DynamicStruct::Builder initResults(kj::Maybe<MessageSize> sizeHint = nullptr); void setResults(DynamicStruct::Reader value); void adoptResults(Orphan<DynamicStruct>&& value); Orphanage getResultsOrphanage(kj::Maybe<MessageSize> sizeHint = nullptr); template <typename SubParams> kj::Promise<void> tailCall(Request<SubParams, DynamicStruct>&& tailRequest); void allowCancellation(); private: CallContextHook* hook; StructSchema paramType; StructSchema resultType; friend class DynamicCapability::Server; }; // ------------------------------------------------------------------- // Make sure ReaderFor<T> and BuilderFor<T> work for DynamicEnum, DynamicStruct, and // DynamicList, so that we can define DynamicValue::as(). template <> struct ReaderFor_ <DynamicEnum, Kind::OTHER> { typedef DynamicEnum Type; }; template <> struct BuilderFor_<DynamicEnum, Kind::OTHER> { typedef DynamicEnum Type; }; template <> struct ReaderFor_ <DynamicStruct, Kind::OTHER> { typedef DynamicStruct::Reader Type; }; template <> struct BuilderFor_<DynamicStruct, Kind::OTHER> { typedef DynamicStruct::Builder Type; }; template <> struct ReaderFor_ <DynamicList, Kind::OTHER> { typedef DynamicList::Reader Type; }; template <> struct BuilderFor_<DynamicList, Kind::OTHER> { typedef DynamicList::Builder Type; }; template <> struct ReaderFor_ <DynamicCapability, Kind::OTHER> { typedef DynamicCapability::Client Type; }; template <> struct BuilderFor_<DynamicCapability, Kind::OTHER> { typedef DynamicCapability::Client Type; }; template <> struct PipelineFor_<DynamicCapability, Kind::OTHER> { typedef DynamicCapability::Client Type; }; class DynamicValue::Reader { public: typedef DynamicValue Reads; inline Reader(decltype(nullptr) n = nullptr); // UNKNOWN inline Reader(Void value); inline Reader(bool value); inline Reader(char value); inline Reader(signed char value); inline Reader(short value); inline Reader(int value); inline Reader(long value); inline Reader(long long value); inline Reader(unsigned char value); inline Reader(unsigned short value); inline Reader(unsigned int value); inline Reader(unsigned long value); inline Reader(unsigned long long value); inline Reader(float value); inline Reader(double value); inline Reader(const char* value); // Text inline Reader(const Text::Reader& value); inline Reader(const Data::Reader& value); inline Reader(const DynamicList::Reader& value); inline Reader(DynamicEnum value); inline Reader(const DynamicStruct::Reader& value); inline Reader(const AnyPointer::Reader& value); inline Reader(DynamicCapability::Client& value); inline Reader(DynamicCapability::Client&& value); template <typename T, typename = kj::EnableIf<kj::canConvert<T*, DynamicCapability::Server*>()>> inline Reader(kj::Own<T>&& value); Reader(ConstSchema constant); template <typename T, typename = decltype(toDynamic(kj::instance<T>()))> inline Reader(T&& value): Reader(toDynamic(kj::mv(value))) {} Reader(const Reader& other); Reader(Reader&& other) noexcept; ~Reader() noexcept(false); Reader& operator=(const Reader& other); Reader& operator=(Reader&& other); // Unfortunately, we cannot use the implicit definitions of these since DynamicCapability is not // trivially copyable. template <typename T> inline ReaderFor<T> as() const { return AsImpl<T>::apply(*this); } // Use to interpret the value as some Cap'n Proto type. Allowed types are: // - Void, bool, [u]int{8,16,32,64}_t, float, double, any enum: Returns the raw value. // - Text, Data, AnyPointer, any struct type: Returns the corresponding Reader. // - List<T> for any T listed above: Returns List<T>::Reader. // - DynamicEnum: Returns the corresponding type. // - DynamicStruct, DynamicList: Returns the corresponding Reader. // - Any capability type, including DynamicCapability: Returns the corresponding Client. // - DynamicValue: Returns an identical Reader. Useful to avoid special-casing in generic code. // (TODO(perf): On GCC 4.8 / Clang 3.3, provide rvalue-qualified version that avoids // refcounting.) // // DynamicValue allows various implicit conversions, mostly just to make the interface friendlier. // - Any integer can be converted to any other integer type so long as the actual value is within // the new type's range. // - Floating-point types can be converted to integers as long as no information would be lost // in the conversion. // - Integers can be converted to floating points. This may lose information, but won't throw. // - Float32/Float64 can be converted between each other. Converting Float64 -> Float32 may lose // information, but won't throw. // - Text can be converted to an enum, if the Text matches one of the enumerant names (but not // vice-versa). // - Capabilities can be upcast (cast to a supertype), but not downcast. // // Any other conversion attempt will throw an exception. inline Type getType() const { return type; } // Get the type of this value. private: Type type; union { Void voidValue; bool boolValue; int64_t intValue; uint64_t uintValue; double floatValue; Text::Reader textValue; Data::Reader dataValue; DynamicList::Reader listValue; DynamicEnum enumValue; DynamicStruct::Reader structValue; AnyPointer::Reader anyPointerValue; mutable DynamicCapability::Client capabilityValue; // Declared mutable because `Client`s normally cannot be const. // Warning: Copy/move constructors assume all these types are trivially copyable except // Capability. }; template <typename T, Kind kind = kind<T>()> struct AsImpl; // Implementation backing the as() method. Needs to be a struct to allow partial // specialization. Has a method apply() which does the work. friend class Orphanage; // to speed up newOrphanCopy(DynamicValue::Reader) }; class DynamicValue::Builder { public: typedef DynamicValue Builds; inline Builder(decltype(nullptr) n = nullptr); // UNKNOWN inline Builder(Void value); inline Builder(bool value); inline Builder(char value); inline Builder(signed char value); inline Builder(short value); inline Builder(int value); inline Builder(long value); inline Builder(long long value); inline Builder(unsigned char value); inline Builder(unsigned short value); inline Builder(unsigned int value); inline Builder(unsigned long value); inline Builder(unsigned long long value); inline Builder(float value); inline Builder(double value); inline Builder(Text::Builder value); inline Builder(Data::Builder value); inline Builder(DynamicList::Builder value); inline Builder(DynamicEnum value); inline Builder(DynamicStruct::Builder value); inline Builder(AnyPointer::Builder value); inline Builder(DynamicCapability::Client& value); inline Builder(DynamicCapability::Client&& value); template <typename T, typename = decltype(toDynamic(kj::instance<T>()))> inline Builder(T value): Builder(toDynamic(value)) {} Builder(Builder& other); Builder(Builder&& other) noexcept; ~Builder() noexcept(false); Builder& operator=(Builder& other); Builder& operator=(Builder&& other); // Unfortunately, we cannot use the implicit definitions of these since DynamicCapability is not // trivially copyable. template <typename T> inline BuilderFor<T> as() { return AsImpl<T>::apply(*this); } // See DynamicValue::Reader::as(). inline Type getType() { return type; } // Get the type of this value. Reader asReader() const; private: Type type; union { Void voidValue; bool boolValue; int64_t intValue; uint64_t uintValue; double floatValue; Text::Builder textValue; Data::Builder dataValue; DynamicList::Builder listValue; DynamicEnum enumValue; DynamicStruct::Builder structValue; AnyPointer::Builder anyPointerValue; mutable DynamicCapability::Client capabilityValue; // Declared mutable because `Client`s normally cannot be const. }; template <typename T, Kind kind = kind<T>()> struct AsImpl; // Implementation backing the as() method. Needs to be a struct to allow partial // specialization. Has a method apply() which does the work. friend class Orphan<DynamicValue>; }; class DynamicValue::Pipeline { public: typedef DynamicValue Pipelines; inline Pipeline(decltype(nullptr) n = nullptr); inline Pipeline(DynamicStruct::Pipeline&& value); inline Pipeline(DynamicCapability::Client&& value); Pipeline(Pipeline&& other) noexcept; Pipeline& operator=(Pipeline&& other); ~Pipeline() noexcept(false); template <typename T> inline PipelineFor<T> releaseAs() { return AsImpl<T>::apply(*this); } inline Type getType() { return type; } // Get the type of this value. private: Type type; union { DynamicStruct::Pipeline structValue; DynamicCapability::Client capabilityValue; }; template <typename T, Kind kind = kind<T>()> struct AsImpl; // Implementation backing the releaseAs() method. Needs to be a struct to allow partial // specialization. Has a method apply() which does the work. }; kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value); kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& value); kj::StringTree KJ_STRINGIFY(DynamicEnum value); kj::StringTree KJ_STRINGIFY(const DynamicStruct::Reader& value); kj::StringTree KJ_STRINGIFY(const DynamicStruct::Builder& value); kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value); kj::StringTree KJ_STRINGIFY(const DynamicList::Builder& value); // ------------------------------------------------------------------- // Orphan <-> Dynamic glue template <> class Orphan<DynamicStruct> { public: Orphan() = default; KJ_DISALLOW_COPY(Orphan); Orphan(Orphan&&) = default; Orphan& operator=(Orphan&&) = default; template <typename T, typename = kj::EnableIf<kind<T>() == Kind::STRUCT>> inline Orphan(Orphan<T>&& other): schema(Schema::from<T>()), builder(kj::mv(other.builder)) {} DynamicStruct::Builder get(); DynamicStruct::Reader getReader() const; template <typename T> Orphan<T> releaseAs(); // Like DynamicStruct::Builder::as(), but coerces the Orphan type. Since Orphans are move-only, // the original Orphan<DynamicStruct> is no longer valid after this call; ownership is // transferred to the returned Orphan<T>. inline bool operator==(decltype(nullptr)) const { return builder == nullptr; } inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; } private: StructSchema schema; _::OrphanBuilder builder; inline Orphan(StructSchema schema, _::OrphanBuilder&& builder) : schema(schema), builder(kj::mv(builder)) {} template <typename, Kind> friend struct _::PointerHelpers; friend struct DynamicList; friend class Orphanage; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; friend class MessageBuilder; }; template <> class Orphan<DynamicList> { public: Orphan() = default; KJ_DISALLOW_COPY(Orphan); Orphan(Orphan&&) = default; Orphan& operator=(Orphan&&) = default; template <typename T, typename = kj::EnableIf<kind<T>() == Kind::LIST>> inline Orphan(Orphan<T>&& other): schema(Schema::from<T>()), builder(kj::mv(other.builder)) {} DynamicList::Builder get(); DynamicList::Reader getReader() const; template <typename T> Orphan<T> releaseAs(); // Like DynamicList::Builder::as(), but coerces the Orphan type. Since Orphans are move-only, // the original Orphan<DynamicStruct> is no longer valid after this call; ownership is // transferred to the returned Orphan<T>. // TODO(someday): Support truncate(). inline bool operator==(decltype(nullptr)) const { return builder == nullptr; } inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; } private: ListSchema schema; _::OrphanBuilder builder; inline Orphan(ListSchema schema, _::OrphanBuilder&& builder) : schema(schema), builder(kj::mv(builder)) {} template <typename, Kind> friend struct _::PointerHelpers; friend struct DynamicList; friend class Orphanage; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; }; template <> class Orphan<DynamicCapability> { public: Orphan() = default; KJ_DISALLOW_COPY(Orphan); Orphan(Orphan&&) = default; Orphan& operator=(Orphan&&) = default; template <typename T, typename = kj::EnableIf<kind<T>() == Kind::INTERFACE>> inline Orphan(Orphan<T>&& other): schema(Schema::from<T>()), builder(kj::mv(other.builder)) {} DynamicCapability::Client get(); DynamicCapability::Client getReader() const; template <typename T> Orphan<T> releaseAs(); // Like DynamicCapability::Client::as(), but coerces the Orphan type. Since Orphans are move-only, // the original Orphan<DynamicCapability> is no longer valid after this call; ownership is // transferred to the returned Orphan<T>. inline bool operator==(decltype(nullptr)) const { return builder == nullptr; } inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; } private: InterfaceSchema schema; _::OrphanBuilder builder; inline Orphan(InterfaceSchema schema, _::OrphanBuilder&& builder) : schema(schema), builder(kj::mv(builder)) {} template <typename, Kind> friend struct _::PointerHelpers; friend struct DynamicList; friend class Orphanage; friend class Orphan<DynamicValue>; friend class Orphan<AnyPointer>; }; template <> class Orphan<DynamicValue> { public: inline Orphan(decltype(nullptr) n = nullptr): type(DynamicValue::UNKNOWN) {} inline Orphan(Void value); inline Orphan(bool value); inline Orphan(char value); inline Orphan(signed char value); inline Orphan(short value); inline Orphan(int value); inline Orphan(long value); inline Orphan(long long value); inline Orphan(unsigned char value); inline Orphan(unsigned short value); inline Orphan(unsigned int value); inline Orphan(unsigned long value); inline Orphan(unsigned long long value); inline Orphan(float value); inline Orphan(double value); inline Orphan(DynamicEnum value); Orphan(Orphan&&) = default; template <typename T> Orphan(Orphan<T>&&); Orphan(Orphan<AnyPointer>&&); Orphan(void*) = delete; // So Orphan(bool) doesn't accept pointers. KJ_DISALLOW_COPY(Orphan); Orphan& operator=(Orphan&&) = default; inline DynamicValue::Type getType() { return type; } DynamicValue::Builder get(); DynamicValue::Reader getReader() const; template <typename T> Orphan<T> releaseAs(); // Like DynamicValue::Builder::as(), but coerces the Orphan type. Since Orphans are move-only, // the original Orphan<DynamicStruct> is no longer valid after this call; ownership is // transferred to the returned Orphan<T>. private: DynamicValue::Type type; union { Void voidValue; bool boolValue; int64_t intValue; uint64_t uintValue; double floatValue; DynamicEnum enumValue; StructSchema structSchema; ListSchema listSchema; InterfaceSchema interfaceSchema; }; _::OrphanBuilder builder; // Only used if `type` is a pointer type. Orphan(DynamicValue::Builder value, _::OrphanBuilder&& builder); Orphan(DynamicValue::Type type, _::OrphanBuilder&& builder) : type(type), builder(kj::mv(builder)) {} Orphan(StructSchema structSchema, _::OrphanBuilder&& builder) : type(DynamicValue::STRUCT), structSchema(structSchema), builder(kj::mv(builder)) {} Orphan(ListSchema listSchema, _::OrphanBuilder&& builder) : type(DynamicValue::LIST), listSchema(listSchema), builder(kj::mv(builder)) {} template <typename, Kind> friend struct _::PointerHelpers; friend struct DynamicStruct; friend struct DynamicList; friend struct AnyPointer; friend class Orphanage; }; template <typename T> inline Orphan<DynamicValue>::Orphan(Orphan<T>&& other) : Orphan(other.get(), kj::mv(other.builder)) {} inline Orphan<DynamicValue>::Orphan(Orphan<AnyPointer>&& other) : type(DynamicValue::ANY_POINTER), builder(kj::mv(other.builder)) {} template <typename T> Orphan<T> Orphan<DynamicStruct>::releaseAs() { get().as<T>(); // type check return Orphan<T>(kj::mv(builder)); } template <typename T> Orphan<T> Orphan<DynamicList>::releaseAs() { get().as<T>(); // type check return Orphan<T>(kj::mv(builder)); } template <typename T> Orphan<T> Orphan<DynamicCapability>::releaseAs() { get().as<T>(); // type check return Orphan<T>(kj::mv(builder)); } template <typename T> Orphan<T> Orphan<DynamicValue>::releaseAs() { get().as<T>(); // type check type = DynamicValue::UNKNOWN; return Orphan<T>(kj::mv(builder)); } template <> Orphan<AnyPointer> Orphan<DynamicValue>::releaseAs<AnyPointer>(); template <> Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>(); template <> Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>(); template <> Orphan<DynamicCapability> Orphan<DynamicValue>::releaseAs<DynamicCapability>(); template <> struct Orphanage::GetInnerBuilder<DynamicStruct, Kind::OTHER> { static inline _::StructBuilder apply(DynamicStruct::Builder& t) { return t.builder; } }; template <> struct Orphanage::GetInnerBuilder<DynamicList, Kind::OTHER> { static inline _::ListBuilder apply(DynamicList::Builder& t) { return t.builder; } }; template <> inline Orphan<DynamicStruct> Orphanage::newOrphanCopy<DynamicStruct::Reader>( DynamicStruct::Reader copyFrom) const { return Orphan<DynamicStruct>( copyFrom.getSchema(), _::OrphanBuilder::copy(arena, capTable, copyFrom.reader)); } template <> inline Orphan<DynamicList> Orphanage::newOrphanCopy<DynamicList::Reader>( DynamicList::Reader copyFrom) const { return Orphan<DynamicList>(copyFrom.getSchema(), _::OrphanBuilder::copy(arena, capTable, copyFrom.reader)); } template <> inline Orphan<DynamicCapability> Orphanage::newOrphanCopy<DynamicCapability::Client>( DynamicCapability::Client copyFrom) const { return Orphan<DynamicCapability>( copyFrom.getSchema(), _::OrphanBuilder::copy(arena, capTable, copyFrom.hook->addRef())); } template <> Orphan<DynamicValue> Orphanage::newOrphanCopy<DynamicValue::Reader>( DynamicValue::Reader copyFrom) const; namespace _ { // private template <> struct PointerHelpers<DynamicStruct, Kind::OTHER> { // getDynamic() is used when an AnyPointer's get() accessor is passed arguments, because for // non-dynamic types PointerHelpers::get() takes a default value as the third argument, and we // don't want people to accidentally be able to provide their own default value. static DynamicStruct::Reader getDynamic(PointerReader reader, StructSchema schema); static DynamicStruct::Builder getDynamic(PointerBuilder builder, StructSchema schema); static void set(PointerBuilder builder, const DynamicStruct::Reader& value); static DynamicStruct::Builder init(PointerBuilder builder, StructSchema schema); static inline void adopt(PointerBuilder builder, Orphan<DynamicStruct>&& value) { builder.adopt(kj::mv(value.builder)); } static inline Orphan<DynamicStruct> disown(PointerBuilder builder, StructSchema schema) { return Orphan<DynamicStruct>(schema, builder.disown()); } }; template <> struct PointerHelpers<DynamicList, Kind::OTHER> { // getDynamic() is used when an AnyPointer's get() accessor is passed arguments, because for // non-dynamic types PointerHelpers::get() takes a default value as the third argument, and we // don't want people to accidentally be able to provide their own default value. static DynamicList::Reader getDynamic(PointerReader reader, ListSchema schema); static DynamicList::Builder getDynamic(PointerBuilder builder, ListSchema schema); static void set(PointerBuilder builder, const DynamicList::Reader& value); static DynamicList::Builder init(PointerBuilder builder, ListSchema schema, uint size); static inline void adopt(PointerBuilder builder, Orphan<DynamicList>&& value) { builder.adopt(kj::mv(value.builder)); } static inline Orphan<DynamicList> disown(PointerBuilder builder, ListSchema schema) { return Orphan<DynamicList>(schema, builder.disown()); } }; template <> struct PointerHelpers<DynamicCapability, Kind::OTHER> { // getDynamic() is used when an AnyPointer's get() accessor is passed arguments, because for // non-dynamic types PointerHelpers::get() takes a default value as the third argument, and we // don't want people to accidentally be able to provide their own default value. static DynamicCapability::Client getDynamic(PointerReader reader, InterfaceSchema schema); static DynamicCapability::Client getDynamic(PointerBuilder builder, InterfaceSchema schema); static void set(PointerBuilder builder, DynamicCapability::Client& value); static void set(PointerBuilder builder, DynamicCapability::Client&& value); static inline void adopt(PointerBuilder builder, Orphan<DynamicCapability>&& value) { builder.adopt(kj::mv(value.builder)); } static inline Orphan<DynamicCapability> disown(PointerBuilder builder, InterfaceSchema schema) { return Orphan<DynamicCapability>(schema, builder.disown()); } }; } // namespace _ (private) template <typename T> inline ReaderFor<T> AnyPointer::Reader::getAs(StructSchema schema) const { return _::PointerHelpers<T>::getDynamic(reader, schema); } template <typename T> inline ReaderFor<T> AnyPointer::Reader::getAs(ListSchema schema) const { return _::PointerHelpers<T>::getDynamic(reader, schema); } template <typename T> inline ReaderFor<T> AnyPointer::Reader::getAs(InterfaceSchema schema) const { return _::PointerHelpers<T>::getDynamic(reader, schema); } template <typename T> inline BuilderFor<T> AnyPointer::Builder::getAs(StructSchema schema) { return _::PointerHelpers<T>::getDynamic(builder, schema); } template <typename T> inline BuilderFor<T> AnyPointer::Builder::getAs(ListSchema schema) { return _::PointerHelpers<T>::getDynamic(builder, schema); } template <typename T> inline BuilderFor<T> AnyPointer::Builder::getAs(InterfaceSchema schema) { return _::PointerHelpers<T>::getDynamic(builder, schema); } template <typename T> inline BuilderFor<T> AnyPointer::Builder::initAs(StructSchema schema) { return _::PointerHelpers<T>::init(builder, schema); } template <typename T> inline BuilderFor<T> AnyPointer::Builder::initAs(ListSchema schema, uint elementCount) { return _::PointerHelpers<T>::init(builder, schema, elementCount); } template <> inline void AnyPointer::Builder::setAs<DynamicStruct>(DynamicStruct::Reader value) { return _::PointerHelpers<DynamicStruct>::set(builder, value); } template <> inline void AnyPointer::Builder::setAs<DynamicList>(DynamicList::Reader value) { return _::PointerHelpers<DynamicList>::set(builder, value); } template <> inline void AnyPointer::Builder::setAs<DynamicCapability>(DynamicCapability::Client value) { return _::PointerHelpers<DynamicCapability>::set(builder, kj::mv(value)); } template <> void AnyPointer::Builder::adopt<DynamicValue>(Orphan<DynamicValue>&& orphan); template <typename T> inline Orphan<T> AnyPointer::Builder::disownAs(StructSchema schema) { return _::PointerHelpers<T>::disown(builder, schema); } template <typename T> inline Orphan<T> AnyPointer::Builder::disownAs(ListSchema schema) { return _::PointerHelpers<T>::disown(builder, schema); } template <typename T> inline Orphan<T> AnyPointer::Builder::disownAs(InterfaceSchema schema) { return _::PointerHelpers<T>::disown(builder, schema); } // We have to declare the methods below inline because Clang and GCC disagree about how to mangle // their symbol names. template <> inline DynamicStruct::Builder Orphan<AnyPointer>::getAs<DynamicStruct>(StructSchema schema) { return DynamicStruct::Builder(schema, builder); } template <> inline DynamicStruct::Reader Orphan<AnyPointer>::getAsReader<DynamicStruct>( StructSchema schema) const { return DynamicStruct::Reader(schema, builder); } template <> inline Orphan<DynamicStruct> Orphan<AnyPointer>::releaseAs<DynamicStruct>(StructSchema schema) { return Orphan<DynamicStruct>(schema, kj::mv(builder)); } template <> inline DynamicList::Builder Orphan<AnyPointer>::getAs<DynamicList>(ListSchema schema) { return DynamicList::Builder(schema, builder); } template <> inline DynamicList::Reader Orphan<AnyPointer>::getAsReader<DynamicList>(ListSchema schema) const { return DynamicList::Reader(schema, builder); } template <> inline Orphan<DynamicList> Orphan<AnyPointer>::releaseAs<DynamicList>(ListSchema schema) { return Orphan<DynamicList>(schema, kj::mv(builder)); } template <> inline DynamicCapability::Client Orphan<AnyPointer>::getAs<DynamicCapability>( InterfaceSchema schema) { return DynamicCapability::Client(schema, builder.asCapability()); } template <> inline DynamicCapability::Client Orphan<AnyPointer>::getAsReader<DynamicCapability>( InterfaceSchema schema) const { return DynamicCapability::Client(schema, builder.asCapability()); } template <> inline Orphan<DynamicCapability> Orphan<AnyPointer>::releaseAs<DynamicCapability>( InterfaceSchema schema) { return Orphan<DynamicCapability>(schema, kj::mv(builder)); } // ======================================================================================= // Inline implementation details. template <typename T> struct ToDynamic_<T, Kind::STRUCT> { static inline DynamicStruct::Reader apply(const typename T::Reader& value) { return DynamicStruct::Reader(Schema::from<T>(), value._reader); } static inline DynamicStruct::Builder apply(typename T::Builder& value) { return DynamicStruct::Builder(Schema::from<T>(), value._builder); } }; template <typename T> struct ToDynamic_<T, Kind::LIST> { static inline DynamicList::Reader apply(const typename T::Reader& value) { return DynamicList::Reader(Schema::from<T>(), value.reader); } static inline DynamicList::Builder apply(typename T::Builder& value) { return DynamicList::Builder(Schema::from<T>(), value.builder); } }; template <typename T> struct ToDynamic_<T, Kind::INTERFACE> { static inline DynamicCapability::Client apply(typename T::Client value) { return DynamicCapability::Client(kj::mv(value)); } static inline DynamicCapability::Client apply(typename T::Client&& value) { return DynamicCapability::Client(kj::mv(value)); } }; template <typename T> ReaderFor<DynamicTypeFor<FromReader<T>>> toDynamic(T&& value) { return ToDynamic_<FromReader<T>>::apply(value); } template <typename T> BuilderFor<DynamicTypeFor<FromBuilder<T>>> toDynamic(T&& value) { return ToDynamic_<FromBuilder<T>>::apply(value); } template <typename T> DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value) { return DynamicEnum(Schema::from<kj::Decay<T>>(), static_cast<uint16_t>(value)); } template <typename T> typename DynamicTypeFor<FromServer<T>>::Client toDynamic(kj::Own<T>&& value) { return typename FromServer<T>::Client(kj::mv(value)); } inline DynamicValue::Reader::Reader(std::nullptr_t n): type(UNKNOWN) {} inline DynamicValue::Builder::Builder(std::nullptr_t n): type(UNKNOWN) {} #define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \ inline DynamicValue::Reader::Reader(cppType value) \ : type(typeTag), fieldName##Value(value) {} \ inline DynamicValue::Builder::Builder(cppType value) \ : type(typeTag), fieldName##Value(value) {} \ inline Orphan<DynamicValue>::Orphan(cppType value) \ : type(DynamicValue::typeTag), fieldName##Value(value) {} CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Void, VOID, void); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(bool, BOOL, bool); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(char, INT, int); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(signed char, INT, int); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(short, INT, int); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(int, INT, int); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(long, INT, int); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(long long, INT, int); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned char, UINT, uint); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned short, UINT, uint); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned int, UINT, uint); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned long, UINT, uint); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned long long, UINT, uint); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(float, FLOAT, float); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(double, FLOAT, float); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicEnum, ENUM, enum); #undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR #define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \ inline DynamicValue::Reader::Reader(const cppType::Reader& value) \ : type(typeTag), fieldName##Value(value) {} \ inline DynamicValue::Builder::Builder(cppType::Builder value) \ : type(typeTag), fieldName##Value(value) {} CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Text, TEXT, text); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Data, DATA, data); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicList, LIST, list); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicStruct, STRUCT, struct); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(AnyPointer, ANY_POINTER, anyPointer); #undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR inline DynamicValue::Reader::Reader(DynamicCapability::Client& value) : type(CAPABILITY), capabilityValue(value) {} inline DynamicValue::Reader::Reader(DynamicCapability::Client&& value) : type(CAPABILITY), capabilityValue(kj::mv(value)) {} template <typename T, typename> inline DynamicValue::Reader::Reader(kj::Own<T>&& value) : type(CAPABILITY), capabilityValue(kj::mv(value)) {} inline DynamicValue::Builder::Builder(DynamicCapability::Client& value) : type(CAPABILITY), capabilityValue(value) {} inline DynamicValue::Builder::Builder(DynamicCapability::Client&& value) : type(CAPABILITY), capabilityValue(kj::mv(value)) {} inline DynamicValue::Reader::Reader(const char* value): Reader(Text::Reader(value)) {} #define CAPNP_DECLARE_TYPE(discrim, typeName) \ template <> \ struct DynamicValue::Reader::AsImpl<typeName> { \ static ReaderFor<typeName> apply(const Reader& reader); \ }; \ template <> \ struct DynamicValue::Builder::AsImpl<typeName> { \ static BuilderFor<typeName> apply(Builder& builder); \ }; //CAPNP_DECLARE_TYPE(VOID, Void) CAPNP_DECLARE_TYPE(BOOL, bool) CAPNP_DECLARE_TYPE(INT8, int8_t) CAPNP_DECLARE_TYPE(INT16, int16_t) CAPNP_DECLARE_TYPE(INT32, int32_t) CAPNP_DECLARE_TYPE(INT64, int64_t) CAPNP_DECLARE_TYPE(UINT8, uint8_t) CAPNP_DECLARE_TYPE(UINT16, uint16_t) CAPNP_DECLARE_TYPE(UINT32, uint32_t) CAPNP_DECLARE_TYPE(UINT64, uint64_t) CAPNP_DECLARE_TYPE(FLOAT32, float) CAPNP_DECLARE_TYPE(FLOAT64, double) CAPNP_DECLARE_TYPE(TEXT, Text) CAPNP_DECLARE_TYPE(DATA, Data) CAPNP_DECLARE_TYPE(LIST, DynamicList) CAPNP_DECLARE_TYPE(STRUCT, DynamicStruct) CAPNP_DECLARE_TYPE(INTERFACE, DynamicCapability) CAPNP_DECLARE_TYPE(ENUM, DynamicEnum) CAPNP_DECLARE_TYPE(ANY_POINTER, AnyPointer) #undef CAPNP_DECLARE_TYPE // CAPNP_DECLARE_TYPE(Void) causes gcc 4.7 to segfault. If I do it manually and remove the // ReaderFor<> and BuilderFor<> wrappers, it works. template <> struct DynamicValue::Reader::AsImpl<Void> { static Void apply(const Reader& reader); }; template <> struct DynamicValue::Builder::AsImpl<Void> { static Void apply(Builder& builder); }; template <typename T> struct DynamicValue::Reader::AsImpl<T, Kind::ENUM> { static T apply(const Reader& reader) { return reader.as<DynamicEnum>().as<T>(); } }; template <typename T> struct DynamicValue::Builder::AsImpl<T, Kind::ENUM> { static T apply(Builder& builder) { return builder.as<DynamicEnum>().as<T>(); } }; template <typename T> struct DynamicValue::Reader::AsImpl<T, Kind::STRUCT> { static typename T::Reader apply(const Reader& reader) { return reader.as<DynamicStruct>().as<T>(); } }; template <typename T> struct DynamicValue::Builder::AsImpl<T, Kind::STRUCT> { static typename T::Builder apply(Builder& builder) { return builder.as<DynamicStruct>().as<T>(); } }; template <typename T> struct DynamicValue::Reader::AsImpl<T, Kind::LIST> { static typename T::Reader apply(const Reader& reader) { return reader.as<DynamicList>().as<T>(); } }; template <typename T> struct DynamicValue::Builder::AsImpl<T, Kind::LIST> { static typename T::Builder apply(Builder& builder) { return builder.as<DynamicList>().as<T>(); } }; template <typename T> struct DynamicValue::Reader::AsImpl<T, Kind::INTERFACE> { static typename T::Client apply(const Reader& reader) { return reader.as<DynamicCapability>().as<T>(); } }; template <typename T> struct DynamicValue::Builder::AsImpl<T, Kind::INTERFACE> { static typename T::Client apply(Builder& builder) { return builder.as<DynamicCapability>().as<T>(); } }; template <> struct DynamicValue::Reader::AsImpl<DynamicValue> { static DynamicValue::Reader apply(const Reader& reader) { return reader; } }; template <> struct DynamicValue::Builder::AsImpl<DynamicValue> { static DynamicValue::Builder apply(Builder& builder) { return builder; } }; inline DynamicValue::Pipeline::Pipeline(std::nullptr_t n): type(UNKNOWN) {} inline DynamicValue::Pipeline::Pipeline(DynamicStruct::Pipeline&& value) : type(STRUCT), structValue(kj::mv(value)) {} inline DynamicValue::Pipeline::Pipeline(DynamicCapability::Client&& value) : type(CAPABILITY), capabilityValue(kj::mv(value)) {} template <typename T> struct DynamicValue::Pipeline::AsImpl<T, Kind::STRUCT> { static typename T::Pipeline apply(Pipeline& pipeline) { return pipeline.releaseAs<DynamicStruct>().releaseAs<T>(); } }; template <typename T> struct DynamicValue::Pipeline::AsImpl<T, Kind::INTERFACE> { static typename T::Client apply(Pipeline& pipeline) { return pipeline.releaseAs<DynamicCapability>().releaseAs<T>(); } }; template <> struct DynamicValue::Pipeline::AsImpl<DynamicStruct, Kind::OTHER> { static PipelineFor<DynamicStruct> apply(Pipeline& pipeline); }; template <> struct DynamicValue::Pipeline::AsImpl<DynamicCapability, Kind::OTHER> { static PipelineFor<DynamicCapability> apply(Pipeline& pipeline); }; // ------------------------------------------------------------------- template <typename T> typename T::Reader DynamicStruct::Reader::as() const { static_assert(kind<T>() == Kind::STRUCT, "DynamicStruct::Reader::as<T>() can only convert to struct types."); schema.requireUsableAs<T>(); return typename T::Reader(reader); } template <typename T> typename T::Builder DynamicStruct::Builder::as() { static_assert(kind<T>() == Kind::STRUCT, "DynamicStruct::Builder::as<T>() can only convert to struct types."); schema.requireUsableAs<T>(); return typename T::Builder(builder); } template <> inline DynamicStruct::Reader DynamicStruct::Reader::as<DynamicStruct>() const { return *this; } template <> inline DynamicStruct::Builder DynamicStruct::Builder::as<DynamicStruct>() { return *this; } inline DynamicStruct::Reader DynamicStruct::Builder::asReader() const { return DynamicStruct::Reader(schema, builder.asReader()); } template <> inline AnyStruct::Reader DynamicStruct::Reader::as<AnyStruct>() const { return AnyStruct::Reader(reader); } template <> inline AnyStruct::Builder DynamicStruct::Builder::as<AnyStruct>() { return AnyStruct::Builder(builder); } template <typename T> typename T::Pipeline DynamicStruct::Pipeline::releaseAs() { static_assert(kind<T>() == Kind::STRUCT, "DynamicStruct::Pipeline::releaseAs<T>() can only convert to struct types."); schema.requireUsableAs<T>(); return typename T::Pipeline(kj::mv(typeless)); } // ------------------------------------------------------------------- template <typename T> typename T::Reader DynamicList::Reader::as() const { static_assert(kind<T>() == Kind::LIST, "DynamicStruct::Reader::as<T>() can only convert to list types."); schema.requireUsableAs<T>(); return typename T::Reader(reader); } template <typename T> typename T::Builder DynamicList::Builder::as() { static_assert(kind<T>() == Kind::LIST, "DynamicStruct::Builder::as<T>() can only convert to list types."); schema.requireUsableAs<T>(); return typename T::Builder(builder); } template <> inline DynamicList::Reader DynamicList::Reader::as<DynamicList>() const { return *this; } template <> inline DynamicList::Builder DynamicList::Builder::as<DynamicList>() { return *this; } template <> inline AnyList::Reader DynamicList::Reader::as<AnyList>() const { return AnyList::Reader(reader); } template <> inline AnyList::Builder DynamicList::Builder::as<AnyList>() { return AnyList::Builder(builder); } // ------------------------------------------------------------------- template <typename T, typename> inline DynamicCapability::Client::Client(T&& client) : Capability::Client(kj::mv(client)), schema(Schema::from<FromClient<T>>()) {} template <typename T, typename> inline DynamicCapability::Client::Client(kj::Own<T>&& server) : Client(server->getSchema(), kj::mv(server)) {} template <typename T> inline DynamicCapability::Client::Client(InterfaceSchema schema, kj::Own<T>&& server) : Capability::Client(kj::mv(server)), schema(schema) {} template <typename T, typename> typename T::Client DynamicCapability::Client::as() { static_assert(kind<T>() == Kind::INTERFACE, "DynamicCapability::Client::as<T>() can only convert to interface types."); schema.requireUsableAs<T>(); return typename T::Client(hook->addRef()); } template <typename T, typename> typename T::Client DynamicCapability::Client::releaseAs() { static_assert(kind<T>() == Kind::INTERFACE, "DynamicCapability::Client::as<T>() can only convert to interface types."); schema.requireUsableAs<T>(); return typename T::Client(kj::mv(hook)); } inline CallContext<DynamicStruct, DynamicStruct>::CallContext( CallContextHook& hook, StructSchema paramType, StructSchema resultType) : hook(&hook), paramType(paramType), resultType(resultType) {} inline DynamicStruct::Reader CallContext<DynamicStruct, DynamicStruct>::getParams() { return hook->getParams().getAs<DynamicStruct>(paramType); } inline void CallContext<DynamicStruct, DynamicStruct>::releaseParams() { hook->releaseParams(); } inline DynamicStruct::Builder CallContext<DynamicStruct, DynamicStruct>::getResults( kj::Maybe<MessageSize> sizeHint) { return hook->getResults(sizeHint).getAs<DynamicStruct>(resultType); } inline DynamicStruct::Builder CallContext<DynamicStruct, DynamicStruct>::initResults( kj::Maybe<MessageSize> sizeHint) { return hook->getResults(sizeHint).initAs<DynamicStruct>(resultType); } inline void CallContext<DynamicStruct, DynamicStruct>::setResults(DynamicStruct::Reader value) { hook->getResults(value.totalSize()).setAs<DynamicStruct>(value); } inline void CallContext<DynamicStruct, DynamicStruct>::adoptResults(Orphan<DynamicStruct>&& value) { hook->getResults(MessageSize { 0, 0 }).adopt(kj::mv(value)); } inline Orphanage CallContext<DynamicStruct, DynamicStruct>::getResultsOrphanage( kj::Maybe<MessageSize> sizeHint) { return Orphanage::getForMessageContaining(hook->getResults(sizeHint)); } template <typename SubParams> inline kj::Promise<void> CallContext<DynamicStruct, DynamicStruct>::tailCall( Request<SubParams, DynamicStruct>&& tailRequest) { return hook->tailCall(kj::mv(tailRequest.hook)); } inline void CallContext<DynamicStruct, DynamicStruct>::allowCancellation() { hook->allowCancellation(); } template <> inline DynamicCapability::Client Capability::Client::castAs<DynamicCapability>( InterfaceSchema schema) { return DynamicCapability::Client(schema, hook->addRef()); } // ------------------------------------------------------------------- template <typename T> ReaderFor<T> ConstSchema::as() const { return DynamicValue::Reader(*this).as<T>(); } } // namespace capnp #endif // CAPNP_DYNAMIC_H_