Commit 18cfa132 authored by Kenton Varda's avatar Kenton Varda

Dynamic API WIP.

parent 1a515be7
// 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.
#define CAPNPROTO_PRIVATE
#include "dynamic.h"
#include "logging.h"
#include <unordered_map>
#include <string>
namespace capnproto {
struct IdTextHash {
static size_t hash(std::pair<uint64_t, Text::Reader> p) {
// djb2a hash, but seeded with ID.
size_t result = p.first;
int c;
const char* str = p.second.c_str();
while ((c = *str++)) {
result = ((result << 5) + result) ^ c; // (result * 33) ^ c
}
return result;
}
};
struct SchemaPool::Impl {
std::unordered_map<uint64_t, schema::Node::Reader> nodeMap;
std::unordered_map<std::pair<uint64_t, std::string>, schema::StructNode::Member::Reader,
IdTextHash>
memberMap;
std::unordered_map<std::pair<uint64_t, std::string>, schema::EnumNode::Enumerant::Reader,
IdTextHash>
enumerantMap;
};
SchemaPool::~SchemaPool() {
delete impl;
}
// TODO(soon): Implement this. Need to copy, ick.
//void add(schema::Node::Reader node) {
//}
void SchemaPool::addNoCopy(schema::Node::Reader node) {
if (impl == nullptr) {
impl = new Impl;
}
// TODO(soon): Check if node is in base.
// TODO(soon): Check if existing node came from generated code.
auto entry = std::make_pair(node.getId(), node);
auto ins = impl->nodeMap.insert(entry);
if (!ins.second) {
// TODO(soon): Check for compatibility.
FAIL_CHECK("TODO: Check schema compatibility when adding.");
}
}
bool SchemaPool::has(uint64_t id) const {
return (impl != nullptr && impl->nodeMap.count(id) != 0) || (base != nullptr && base->has(id));
}
DynamicStruct::Reader SchemaPool::getRoot(MessageReader& message, uint64_t typeId) const {
}
DynamicStruct::Builder SchemaPool::getRoot(MessageBuilder& message, uint64_t typeId) const {
}
// =======================================================================================
namespace {
template <typename T, typename U>
CAPNPROTO_ALWAYS_INLINE(T bitCast(U value));
template <typename T, typename U>
inline T bitCast(U value) {
static_assert(sizeof(T) == sizeof(U), "Size must match.");
return value;
}
template <>
inline float bitCast<float, uint32_t>(uint32_t value) {
float result;
memcpy(&result, &value, sizeof(value));
return result;
}
template <>
inline double bitCast<double, uint64_t>(uint64_t value) {
double result;
memcpy(&result, &value, sizeof(value));
return result;
}
template <>
inline uint32_t bitCast<uint32_t, float>(float value) {
uint32_t result;
memcpy(&result, &value, sizeof(value));
return result;
}
template <>
inline uint64_t bitCast<uint64_t, double>(double value) {
uint64_t result;
memcpy(&result, &value, sizeof(value));
return result;
}
internal::FieldSize elementSizeFor(schema::Type::Reader elementType) {
switch (elementType.getBody().which()) {
case schema::Type::Body::VOID_TYPE: return internal::FieldSize::VOID;
case schema::Type::Body::BOOL_TYPE: return internal::FieldSize::BIT;
case schema::Type::Body::INT8_TYPE: return internal::FieldSize::BYTE;
case schema::Type::Body::INT16_TYPE: return internal::FieldSize::TWO_BYTES;
case schema::Type::Body::INT32_TYPE: return internal::FieldSize::FOUR_BYTES;
case schema::Type::Body::INT64_TYPE: return internal::FieldSize::EIGHT_BYTES;
case schema::Type::Body::UINT8_TYPE: return internal::FieldSize::BYTE;
case schema::Type::Body::UINT16_TYPE: return internal::FieldSize::TWO_BYTES;
case schema::Type::Body::UINT32_TYPE: return internal::FieldSize::FOUR_BYTES;
case schema::Type::Body::UINT64_TYPE: return internal::FieldSize::EIGHT_BYTES;
case schema::Type::Body::FLOAT32_TYPE: return internal::FieldSize::FOUR_BYTES;
case schema::Type::Body::FLOAT64_TYPE: return internal::FieldSize::EIGHT_BYTES;
case schema::Type::Body::TEXT_TYPE: return internal::FieldSize::REFERENCE;
case schema::Type::Body::DATA_TYPE: return internal::FieldSize::REFERENCE;
case schema::Type::Body::LIST_TYPE: return internal::FieldSize::REFERENCE;
case schema::Type::Body::ENUM_TYPE: return internal::FieldSize::TWO_BYTES;
case schema::Type::Body::STRUCT_TYPE: return internal::FieldSize::INLINE_COMPOSITE;
case schema::Type::Body::INTERFACE_TYPE: return internal::FieldSize::REFERENCE;
case schema::Type::Body::OBJECT_TYPE: FAIL_CHECK("List(Object) not supported.");
}
FAIL_CHECK("Can't get here.");
return internal::FieldSize::VOID;
}
} // namespace
// =======================================================================================
schema::EnumNode::Reader DynamicEnum::getSchema() {
return schema.getBody().getEnumNode();
}
Maybe<schema::EnumNode::Enumerant::Reader> DynamicEnum::getEnumerant() {
auto enumerants = getSchema().getEnumerants();
if (value < enumerants.size()) {
return enumerants[value];
} else {
return nullptr;
}
}
Maybe<schema::EnumNode::Enumerant::Reader> DynamicEnum::findEnumerantByName(Text::Reader name) {
auto iter = pool->impl->enumerantMap.find(std::make_pair(schema.getId(), name));
if (iter == pool->impl->enumerantMap.end()) {
return nullptr;
} else {
return iter->second;
}
}
uint16_t DynamicEnum::toImpl(uint64_t requestedTypeId) {
VALIDATE_INPUT(requestedTypeId == schema.getId(), "Type mismatch in DynamicEnum.to().") {
// Go on with value.
}
return value;
}
// =======================================================================================
DynamicStruct::Reader DynamicObject::Reader::toStruct(schema::Node::Reader schema) {
PRECOND(schema.getBody().which() == schema::Node::Body::STRUCT_NODE,
"toStruct() passed a non-struct schema.");
if (reader.kind == internal::ObjectKind::NULL_POINTER) {
return DynamicStruct::Reader(pool, schema, internal::StructReader());
}
VALIDATE_INPUT(reader.kind == internal::ObjectKind::STRUCT, "Object is not a struct.") {
return DynamicStruct::Reader(pool, schema, internal::StructReader());
}
return DynamicStruct::Reader(pool, schema, reader.structReader);
}
DynamicStruct::Builder DynamicObject::Builder::toStruct(schema::Node::Reader schema) {
PRECOND(schema.getBody().which() == schema::Node::Body::STRUCT_NODE,
"toStruct() passed a non-struct schema.");
if (builder.kind == internal::ObjectKind::NULL_POINTER) {
return DynamicStruct::Builder(pool, schema, internal::StructBuilder());
}
VALIDATE_INPUT(builder.kind == internal::ObjectKind::STRUCT, "Object is not a struct.") {
return DynamicStruct::Builder(pool, schema, internal::StructBuilder());
}
return DynamicStruct::Builder(pool, schema, builder.structBuilder);
}
DynamicStruct::Reader DynamicObject::Reader::toStruct(uint64_t typeId) {
return toStruct(pool->getStruct(typeId));
}
DynamicStruct::Builder DynamicObject::Builder::toStruct(uint64_t typeId) {
return toStruct(pool->getStruct(typeId));
}
DynamicList::Reader DynamicObject::Reader::toList(schema::Type::Reader elementType) {
return toList(internal::ListSchema(elementType));
}
DynamicList::Builder DynamicObject::Builder::toList(schema::Type::Reader elementType) {
return toList(internal::ListSchema(elementType));
}
DynamicList::Reader DynamicObject::Reader::toList(internal::ListSchema schema) {
if (reader.kind == internal::ObjectKind::NULL_POINTER) {
return DynamicList::Reader(pool, schema, internal::ListReader());
}
VALIDATE_INPUT(reader.kind == internal::ObjectKind::LIST, "Object is not a list.") {
return DynamicList::Reader(pool, schema, internal::ListReader());
}
return DynamicList::Reader(pool, schema, reader.listReader);
}
DynamicList::Builder DynamicObject::Builder::toList(internal::ListSchema schema) {
if (builder.kind == internal::ObjectKind::NULL_POINTER) {
return DynamicList::Builder(pool, schema, internal::ListBuilder());
}
VALIDATE_INPUT(builder.kind == internal::ObjectKind::LIST, "Object is not a list.") {
return DynamicList::Builder(pool, schema, internal::ListBuilder());
}
return DynamicList::Builder(pool, schema, builder.listBuilder);
}
// =======================================================================================
schema::StructNode::Union::Reader DynamicUnion::Reader::getSchema() {
return schema.getBody().getUnionMember();
}
schema::StructNode::Union::Reader DynamicUnion::Builder::getSchema() {
return schema.getBody().getUnionMember();
}
Maybe<schema::StructNode::Member::Reader> DynamicUnion::Reader::which() {
auto uschema = getSchema();
auto members = uschema.getMembers();
uint16_t discrim = reader.getDataField<uint32_t>(uschema.getDiscriminantOffset() * ELEMENTS);
if (discrim < members.size()) {
return members[discrim];
} else {
return nullptr;
}
}
Maybe<schema::StructNode::Member::Reader> DynamicUnion::Builder::which() {
auto uschema = getSchema();
auto members = uschema.getMembers();
uint16_t discrim = builder.getDataField<uint32_t>(uschema.getDiscriminantOffset() * ELEMENTS);
if (discrim < members.size()) {
return members[discrim];
} else {
return nullptr;
}
}
DynamicValue::Reader DynamicUnion::Reader::get() {
auto w = which();
RECOVERABLE_PRECOND(w != nullptr, "Can't get() unknown union value.") {
return DynamicValue::Reader(Void::VOID);
}
auto body = w->getBody();
CHECK(body.which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Unsupported union member type.");
return DynamicValue::Reader(DynamicStruct::Reader::getFieldImpl(
pool, reader, body.getFieldMember()));
}
DynamicValue::Builder DynamicUnion::Builder::get() {
auto w = which();
RECOVERABLE_PRECOND(w != nullptr, "Can't get() unknown union value.") {
return DynamicValue::Builder(Void::VOID);
}
auto body = w->getBody();
CHECK(body.which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Unsupported union member type.");
return DynamicValue::Builder(DynamicStruct::Builder::getFieldImpl(
pool, builder, body.getFieldMember()));
}
void DynamicUnion::Builder::set(
schema::StructNode::Field::Reader field, DynamicValue::Reader value) {
auto uschema = getSchema();
builder.setDataField<uint16_t>(uschema.getTagOffset(), field.getIndex());
DynamicStruct::Builder::setFieldImpl(pool, builder, field, value);
}
// =======================================================================================
DynamicValue::Reader DynamicStruct::Reader::getFieldImpl(
const SchemaPool* pool, internal::StructReader reader,
schema::StructNode::Field::Reader field) {
auto type = field.getType().getBody();
auto dval = field.getDefaultValue().getBody();
switch (type.which()) {
case schema::Type::Body::VOID_TYPE:
return DynamicValue::Reader(reader.getDataField<Void>(field.getOffset() * ELEMENTS));
#define HANDLE_TYPE(discrim, titleCase, type) \
case schema::Type::Body::discrim##_TYPE: \
return DynamicValue::Reader(reader.getDataField<type>( \
field.getOffset() * ELEMENTS, \
bitCast<typename internal::MaskType<type>::Type>(dval.get##titleCase##Value())));
HANDLE_TYPE(BOOL, Bool, bool)
HANDLE_TYPE(INT8, Int8, int8_t)
HANDLE_TYPE(INT16, Int16, int16_t)
HANDLE_TYPE(INT32, Int32, int32_t)
HANDLE_TYPE(INT64, Int64, int64_t)
HANDLE_TYPE(UINT8, Uint8, uint8_t)
HANDLE_TYPE(UINT16, Uint16, uint16_t)
HANDLE_TYPE(UINT32, Uint32, uint32_t)
HANDLE_TYPE(UINT64, Uint64, uint64_t)
HANDLE_TYPE(FLOAT32, Float32, float)
HANDLE_TYPE(FLOAT64, Float64, double)
#undef HANDLE_TYPE
case schema::Type::Body::ENUM_TYPE: {
uint16_t typedDval;
typedDval = dval.getEnumValue();
return DynamicValue::Reader(DynamicEnum(
pool, pool->getEnum(type.getEnumType()),
reader.getDataField<uint16_t>(field.getOffset() * ELEMENTS, typedDval)));
}
case schema::Type::Body::TEXT_TYPE: {
Text::Reader typedDval = dval.getTextValue();
return DynamicValue::Reader(
reader.getBlobField<Text>(field.getOffset() * REFERENCES,
typedDval.data(), typedDval.size() * BYTES));
}
case schema::Type::Body::DATA_TYPE: {
Data::Reader typedDval = dval.getDataValue();
return DynamicValue::Reader(
reader.getBlobField<Data>(field.getOffset() * REFERENCES,
typedDval.data(), typedDval.size() * BYTES));
}
case schema::Type::Body::LIST_TYPE: {
auto elementType = type.getListType();
return DynamicValue::Reader(DynamicList::Reader(
pool, elementType,
reader.getListField(field.getOffset() * REFERENCES,
elementSizeFor(elementType),
dval.getListValue<internal::TrustedMessage>())));
}
case schema::Type::Body::STRUCT_TYPE: {
return DynamicValue::Reader(DynamicStruct::Reader(
pool, pool->getStruct(type.getStructType()),
reader.getStructField(field.getOffset() * REFERENCES,
dval.getStructValue<internal::TrustedMessage>())));
}
case schema::Type::Body::OBJECT_TYPE: {
return DynamicValue::Reader(DynamicObject::Reader(
pool, reader.getObjectField(field.getOffset() * REFERENCES,
dval.getObjectValue<internal::TrustedMessage>())));
}
case schema::Type::Body::INTERFACE_TYPE:
FAIL_CHECK("Interfaces not yet implemented.");
break;
}
FAIL_CHECK("Can't get here.");
return DynamicValue::Reader();
}
DynamicValue::Builder DynamicStruct::Builder::getFieldImpl(
const SchemaPool* pool, internal::StructBuilder builder,
schema::StructNode::Field::Reader field) {
auto type = field.getType().getBody();
auto dval = field.getDefaultValue().getBody();
switch (type.which()) {
case schema::Type::Body::VOID_TYPE:
return DynamicValue::Builder(builder.getDataField<Void>(field.getOffset() * ELEMENTS));
#define HANDLE_TYPE(discrim, titleCase, type) \
case schema::Type::Body::discrim##_TYPE: \
return DynamicValue::Builder(builder.getDataField<type>( \
field.getOffset() * ELEMENTS, \
bitCast<typename internal::MaskType<type>::Type>(dval.get##titleCase##Value())));
HANDLE_TYPE(BOOL, Bool, bool)
HANDLE_TYPE(INT8, Int8, int8_t)
HANDLE_TYPE(INT16, Int16, int16_t)
HANDLE_TYPE(INT32, Int32, int32_t)
HANDLE_TYPE(INT64, Int64, int64_t)
HANDLE_TYPE(UINT8, Uint8, uint8_t)
HANDLE_TYPE(UINT16, Uint16, uint16_t)
HANDLE_TYPE(UINT32, Uint32, uint32_t)
HANDLE_TYPE(UINT64, Uint64, uint64_t)
HANDLE_TYPE(FLOAT32, Float32, float)
HANDLE_TYPE(FLOAT64, Float64, double)
#undef HANDLE_TYPE
case schema::Type::Body::ENUM_TYPE: {
uint16_t typedDval;
typedDval = dval.getEnumValue();
return DynamicValue::Builder(DynamicEnum(
pool, pool->getEnum(type.getEnumType()),
builder.getDataField<uint16_t>(field.getOffset() * ELEMENTS, typedDval)));
}
case schema::Type::Body::TEXT_TYPE: {
Text::Reader typedDval = dval.getTextValue();
return DynamicValue::Builder(
builder.getBlobField<Text>(field.getOffset() * REFERENCES,
typedDval.data(), typedDval.size() * BYTES));
}
case schema::Type::Body::DATA_TYPE: {
Data::Reader typedDval = dval.getDataValue();
return DynamicValue::Builder(
builder.getBlobField<Data>(field.getOffset() * REFERENCES,
typedDval.data(), typedDval.size() * BYTES));
}
case schema::Type::Body::LIST_TYPE: {
auto elementType = type.getListType();
return DynamicValue::Builder(DynamicList::Builder(
pool, elementType,
builder.getListField(field.getOffset() * REFERENCES,
dval.getListValue<internal::TrustedMessage>())));
}
case schema::Type::Body::STRUCT_TYPE: {
auto structNode = pool->getStruct(type.getStructType());
auto structSchema = structNode.getBody().getStructNode();
return DynamicValue::Builder(DynamicStruct::Builder(
pool, structNode,
builder.getStructField(
field.getOffset() * REFERENCES,
internal::StructSize(
structSchema.getDataSectionWordSize() * WORDS,
structSchema.getPointerSectionSize() * REFERENCES,
static_cast<internal::FieldSize>(structSchema.getPreferredListEncoding())),
dval.getStructValue<internal::TrustedMessage>())));
}
case schema::Type::Body::OBJECT_TYPE: {
return DynamicValue::Builder(DynamicObject::Builder(
pool, builder.getObjectField(field.getOffset() * REFERENCES,
dval.getObjectValue<internal::TrustedMessage>())));
}
case schema::Type::Body::INTERFACE_TYPE:
FAIL_CHECK("Interfaces not yet implemented.");
break;
}
FAIL_CHECK("Can't get here.");
return DynamicValue::Builder();
}
void DynamicStruct::Builder::setFieldImpl(
const SchemaPool* pool, internal::StructBuilder builder,
schema::StructNode::Field::Reader field, DynamicValue::Reader value) {
auto type = field.getType().getBody();
auto dval = field.getDefaultValue().getBody();
switch (type.which()) {
case schema::Type::Body::VOID_TYPE:
builder.setDataField<Void>(field.getOffset() * ELEMENTS, value.to<Void>());
break;
#define HANDLE_TYPE(discrim, titleCase, type) \
case schema::Type::Body::discrim##_TYPE: \
builder.setDataField<type>( \
field.getOffset() * ELEMENTS, value.to<type>(), \
bitCast<internal::Mask<type> >(dval.get##titleCase##Value()));
break;
HANDLE_TYPE(BOOL, Bool, bool)
HANDLE_TYPE(INT8, Int8, int8_t)
HANDLE_TYPE(INT16, Int16, int16_t)
HANDLE_TYPE(INT32, Int32, int32_t)
HANDLE_TYPE(INT64, Int64, int64_t)
HANDLE_TYPE(UINT8, Uint8, uint8_t)
HANDLE_TYPE(UINT16, Uint16, uint16_t)
HANDLE_TYPE(UINT32, Uint32, uint32_t)
HANDLE_TYPE(UINT64, Uint64, uint64_t)
HANDLE_TYPE(FLOAT32, Float32, float)
HANDLE_TYPE(FLOAT64, Float64, double)
#undef HANDLE_TYPE
case schema::Type::Body::ENUM_TYPE:
builder.setDataField<uint16_t>(
field.getOffset() * ELEMENTS, value.to<DynamicEnum>().getRaw(),
dval.getEnumValue());
break;
case schema::Type::Body::TEXT_TYPE:
builder.setBlobField<Text>(field.getOffset() * REFERENCES, value.to<Text>());
break;
case schema::Type::Body::DATA_TYPE:
builder.setBlobField<Data>(field.getOffset() * REFERENCES, value.to<Data>());
break;
case schema::Type::Body::LIST_TYPE: {
// TODO(soon): We need to do a schemaless copy to avoid losing information if the values are
// larger than what the schema defines.
auto listValue = value.to<DynamicList>();
initListFieldImpl(pool, builder, field, listValue.size()).copyFrom(listValue);
break;
}
case schema::Type::Body::STRUCT_TYPE: {
// TODO(soon): We need to do a schemaless copy to avoid losing information if the values are
// larger than what the schema defines.
initStructFieldImpl(pool, builder, field).copyFrom(value.to<DynamicStruct>());
break;
}
case schema::Type::Body::OBJECT_TYPE: {
// TODO(soon): Perform schemaless copy.
FAIL_CHECK("TODO");
break;
}
case schema::Type::Body::INTERFACE_TYPE:
FAIL_CHECK("Interfaces not yet implemented.");
break;
}
}
// =======================================================================================
#if 0
#define HANDLE_TYPE(name, discrim, typeName) \
ReaderFor<typeName> DynamicValue::Reader::ToImpl<typeName>::apply(Reader reader) { \
VALIDATE_INPUT(reader.type == schema::Type::Body::discrim##_TYPE, \
"Type mismatch when using DynamicValue::to().") { \
return typeName(); \
} \
return reader.name##Value; \
} \
BuilderFor<typeName> DynamicValue::Builder::ToImpl<typeName>::apply(Builder builder) { \
VALIDATE_INPUT(builder.type == schema::Type::Body::discrim##_TYPE, \
"Type mismatch when using DynamicValue::to().") { \
return typeName(); \
} \
return builder.name##Value; \
}
//HANDLE_TYPE(void, VOID, Void)
HANDLE_TYPE(bool, BOOL, bool)
HANDLE_TYPE(int8, INT8, int8_t)
HANDLE_TYPE(int16, INT16, int16_t)
HANDLE_TYPE(int32, INT32, int32_t)
HANDLE_TYPE(int64, INT64, int64_t)
HANDLE_TYPE(uint8, UINT8, uint8_t)
HANDLE_TYPE(uint16, UINT16, uint16_t)
HANDLE_TYPE(uint32, UINT32, uint32_t)
HANDLE_TYPE(uint64, UINT64, uint64_t)
HANDLE_TYPE(float32, FLOAT32, float)
HANDLE_TYPE(float64, FLOAT64, double)
HANDLE_TYPE(text, TEXT, Text)
HANDLE_TYPE(data, DATA, Data)
HANDLE_TYPE(list, LIST, DynamicList)
HANDLE_TYPE(struct, STRUCT, DynamicStruct)
HANDLE_TYPE(enum, ENUM, DynamicEnum)
HANDLE_TYPE(object, OBJECT, DynamicObject)
#undef HANDLE_TYPE
#endif
// As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7.
Void DynamicValue::Reader::ToImpl<Void>::apply(Reader reader) {
VALIDATE_INPUT(reader.type == schema::Type::Body::VOID_TYPE,
"Type mismatch when using DynamicValue::to().") {
return Void();
}
return reader.voidValue;
}
Void DynamicValue::Builder::ToImpl<Void>::apply(Builder builder) {
VALIDATE_INPUT(builder.type == schema::Type::Body::VOID_TYPE,
"Type mismatch when using DynamicValue::to().") {
return Void();
}
return builder.voidValue;
}
} // namespace capnproto
// 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.
// 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 CAPNPROTO_DYNAMIC_H_
#define CAPNPROTO_DYNAMIC_H_
#include "schema.capnp.h"
#include "layout.h"
namespace capnproto {
class MessageReader;
class MessageBuilder;
struct DynamicValue {
class Reader;
class Builder;
};
class DynamicEnum;
struct DynamicObject {
class Reader;
class Builder;
};
struct DynamicUnion {
class Reader;
class Builder;
};
struct DynamicStruct {
class Reader;
class Builder;
};
struct DynamicList {
class Reader;
class Builder;
};
class SchemaPool {
// Class representing a pool of schema data which is indexed for convenient traversal.
//
// TODO(someday): Allow registration of a callback to look up missing nodes. The callback
// should not return a value, but instead should receive a pointer to the pool to populate.
// This will make it easier to deal with ownership and to batch-add related nodes.
public:
SchemaPool(): base(nullptr), impl(nullptr) {}
// Constructs an empty pool.
explicit SchemaPool(const SchemaPool& base): base(&base), impl(nullptr) {}
// Constructs a pool that extends the given base pool. The behavior is similar to copying the
// base pool, except that memory is shared. The base pool must outlive the new pool.
//
// The purpose of this is to allow a pool to be shared by multiple threads. The from*() methods
// may add new schema nodes to the pool if they aren't already present, and therefore they are
// not thread-safe. However, if you create a shared pool that contains all the types you need,
// then it is reasonably efficient to create a fresh SchemaPool on the stack extending the shared
// pool each time you need to manipulate a type dynamically. If the shared pool ends up
// containing everything that is needed, then the extension pool won't allocate any memory at all.
~SchemaPool();
void add(schema::Node::Reader node);
// Add a schema node. It will be copied and validated, throwing an exception if invalid. If
// another node with the same ID already exists, the nodes will be compared for compatibility
// and the definition determined to be "newer" will be kept. If the nodes are not compatible,
// an exception will be thrown.
template <typename T>
void add();
// Add schema for the given compiled-in type and all of its transitive dependencies, including
// nested nodes, but NOT necessarily including annotation definitions (because those are not
// always compiled in) or parent scopes (because adding parent scopes would necessarily mean
// adding all types in the file and in all transitive imports, which may be much more than you
// want).
bool has(uint64_t typeId) const;
template <typename T> DynamicEnum fromEnum(T&& value);
template <typename T> DynamicStruct::Reader fromStructReader(T&& reader);
template <typename T> DynamicStruct::Builder fromStructBuilder(T&& builder);
template <typename T> DynamicList::Reader fromListReader(T&& reader);
template <typename T> DynamicList::Builder fromListBuilder(T&& builder);
DynamicStruct::Reader getRoot(MessageReader& message, uint64_t typeId) const;
DynamicStruct::Builder getRoot(MessageBuilder& message, uint64_t typeId) const;
private:
struct Impl;
const SchemaPool* base;
Impl* impl;
SchemaPool& operator=(const SchemaPool&) = delete;
void addNoCopy(schema::Node::Reader node);
schema::Node::Reader getStruct(uint64_t id) const;
schema::Node::Reader getEnum(uint64_t id) const;
schema::Node::Reader getInterface(uint64_t id) const;
friend class DynamicEnum;
friend class DynamicStruct;
friend class DynamicList;
friend class DynamicObject;
};
// -------------------------------------------------------------------
namespace internal {
struct ListSchema {
// Hack for determining/specifying the schema for a List without having to construct a Cap'n Proto
// message.
schema::Type::Body::Which elementType;
uint8_t nestingDepth; // 0 for T, 1 for List(T), 2 for List(List(T)), ...
uint64_t elementTypeId;
constexpr ListSchema(schema::Type::Body::Which elementType)
: elementType(elementType), nestingDepth(0), elementTypeId(0) {}
constexpr ListSchema(schema::Type::Body::Which elementType, uint64_t elementTypeId)
: elementType(elementType), nestingDepth(0), elementTypeId(elementTypeId) {}
constexpr ListSchema(schema::Type::Body::Which elementType, uint8_t nestingDepth,
uint64_t elementTypeId)
: elementType(elementType), nestingDepth(nestingDepth), elementTypeId(elementTypeId) {}
ListSchema(schema::Type::Reader elementType);
// Construct from an actual schema.
constexpr ListSchema deeper() {
return ListSchema(elementType, nestingDepth + 1, elementTypeId);
}
};
template <typename ElementType, Kind kind = kind<ElementType>()>
struct ListSchemaFor;
#define CAPNPROTO_DECLARE_TYPE(discrim, typeName) \
template <> \
struct ListSchemaFor<List<typeName>> { \
static constexpr ListSchema type = ListSchema(schema::Type::Body::discrim##_TYPE); \
};
CAPNPROTO_DECLARE_TYPE(VOID, Void)
CAPNPROTO_DECLARE_TYPE(BOOL, bool)
CAPNPROTO_DECLARE_TYPE(INT8, int8_t)
CAPNPROTO_DECLARE_TYPE(INT16, int16_t)
CAPNPROTO_DECLARE_TYPE(INT32, int32_t)
CAPNPROTO_DECLARE_TYPE(INT64, int64_t)
CAPNPROTO_DECLARE_TYPE(UINT8, uint8_t)
CAPNPROTO_DECLARE_TYPE(UINT16, uint16_t)
CAPNPROTO_DECLARE_TYPE(UINT32, uint32_t)
CAPNPROTO_DECLARE_TYPE(UINT64, uint64_t)
CAPNPROTO_DECLARE_TYPE(FLOAT32, float)
CAPNPROTO_DECLARE_TYPE(FLOAT64, double)
CAPNPROTO_DECLARE_TYPE(TEXT, Text)
CAPNPROTO_DECLARE_TYPE(DATA, Data)
CAPNPROTO_DECLARE_TYPE(LIST, DynamicList)
#undef CAPNPROTO_DECLARE_TYPE
template <typename T>
struct ListSchemaFor<T, Kind::ENUM> {
static constexpr ListSchema type = ListSchema(schema::Type::Body::ENUM_TYPE, typeId<T>());
};
template <typename T>
struct ListSchemaFor<T, Kind::STRUCT> {
static constexpr ListSchema type = ListSchema(schema::Type::Body::STRUCT_TYPE, typeId<T>());
};
template <typename T>
struct ListSchemaFor<T, Kind::INTERFACE> {
static constexpr ListSchema type = ListSchema(schema::Type::Body::INTERFACE_TYPE, typeId<T>());
};
template <typename T>
struct ListSchemaFor<List<T>, Kind::LIST> {
static constexpr ListSchema type = ListSchemaFor<T>::schema.deeper();
};
} // namespace internal
// -------------------------------------------------------------------
class DynamicEnum {
public:
DynamicEnum() = default;
template <typename T>
inline T to() { return static_cast<T>(toImpl(typeId<T>())); }
// Cast to a native enum type.
schema::Node::Reader getSchemaNode() { return schema; }
schema::EnumNode::Reader getSchema();
Maybe<schema::EnumNode::Enumerant::Reader> getEnumerant();
// 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.
Maybe<schema::EnumNode::Enumerant::Reader> findEnumerantByName(Text::Reader name);
// Search this enum's type for an enumerant with the given name.
inline uint16_t getRaw() { return value; }
// Returns the raw underlying enum value.
private:
const SchemaPool* pool;
schema::Node::Reader schema;
uint16_t value;
inline DynamicEnum(const SchemaPool* pool, schema::Node::Reader schema, uint16_t value)
: pool(pool), schema(schema), value(value) {}
uint16_t toImpl(uint64_t requestedTypeId);
friend struct DynamicStruct;
};
// -------------------------------------------------------------------
class DynamicObject::Reader {
public:
Reader() = default;
template <typename T>
inline typename T::Reader to() { return ToImpl<T>::apply(*this); }
// Convert the object to the given struct, list, or blob type.
DynamicStruct::Reader toStruct(schema::Node::Reader schema);
DynamicList::Reader toList(schema::Type::Reader elementType);
private:
const SchemaPool* pool;
internal::ObjectReader reader;
inline Reader(const SchemaPool* pool, internal::ObjectReader reader)
: pool(pool), reader(reader) {}
template <typename T, Kind kind = kind<T>()> struct ToImpl;
// Implementation backing the to() method. Needs to be a struct to allow partial
// specialization. Has a method apply() which does the work.
DynamicStruct::Reader toStruct(uint64_t typeId);
DynamicList::Reader toList(internal::ListSchema schema);
friend struct DynamicStruct;
};
class DynamicObject::Builder {
public:
Builder() = default;
template <typename T>
inline typename T::Builder to() { return ToImpl<T>::apply(*this); }
// Convert the object to the given struct, list, or blob type.
DynamicStruct::Builder toStruct(schema::Node::Reader schema);
DynamicList::Builder toList(schema::Type::Reader elementType);
private:
const SchemaPool* pool;
internal::ObjectBuilder builder;
inline Builder(const SchemaPool* pool, internal::ObjectBuilder builder)
: pool(pool), builder(builder) {}
template <typename T, Kind kind = kind<T>()> struct ToImpl;
// Implementation backing the to() method. Needs to be a struct to allow partial
// specialization. Has a method apply() which does the work.
DynamicStruct::Builder toStruct(uint64_t typeId);
DynamicList::Builder toList(internal::ListSchema schema);
friend struct DynamicStruct;
};
// -------------------------------------------------------------------
class DynamicUnion::Reader {
public:
Reader() = default;
schema::StructNode::Member::Reader getMemberSchema() { return schema; }
schema::StructNode::Union::Reader getSchema();
Maybe<schema::StructNode::Member::Reader> which();
// Returns which field is set, or nullptr if an unknown field is set (i.e. the schema is old, and
// the underlying data has the union set to a member we don't know about).
DynamicValue::Reader get();
// Get the value of whichever field of the union is set.
private:
const SchemaPool* pool;
schema::StructNode::Member::Reader schema;
internal::StructReader reader;
inline Reader(const SchemaPool* pool, schema::StructNode::Member::Reader schema,
internal::StructReader reader)
: pool(pool), schema(schema), reader(reader) {}
};
class DynamicUnion::Builder {
public:
Builder() = default;
schema::StructNode::Member::Reader getMemberSchema() { return schema; }
schema::StructNode::Union::Reader getSchema();
Maybe<schema::StructNode::Member::Reader> which();
// Returns which field is set, or nullptr if an unknown field is set (i.e. the schema is old, and
// the underlying data has the union set to a member we don't know about).
DynamicValue::Builder get();
void set(schema::StructNode::Field::Reader field, DynamicValue::Reader value);
DynamicValue::Builder init(schema::StructNode::Field::Reader field);
private:
const SchemaPool* pool;
schema::StructNode::Member::Reader schema;
internal::StructBuilder builder;
inline Builder(const SchemaPool* pool, schema::StructNode::Member::Reader schema,
internal::StructBuilder builder)
: pool(pool), schema(schema), builder(builder) {}
};
// -------------------------------------------------------------------
class DynamicStruct::Reader {
public:
Reader() = default;
template <typename T>
typename T::Reader to();
// Convert the dynamic struct to its compiled-in type.
schema::Node::Reader getSchemaNode();
schema::StructNode::Reader getSchema();
Maybe<schema::StructNode::Member::Reader> findMemberByName(Text::Reader name);
// Looks up the member with the given name, or returns nullptr if no such member exists.
DynamicValue::Reader getField(schema::StructNode::Field::Reader field);
// Returns the value of the given field.
DynamicUnion::Reader getUnion(schema::StructNode::Union::Reader un);
// Returns the value of the given union.
private:
const SchemaPool* pool;
schema::Node::Reader schema;
internal::StructReader reader;
inline Reader(const SchemaPool* pool, schema::Node::Reader schema, internal::StructReader reader)
: pool(pool), schema(schema), reader(reader) {}
static DynamicValue::Reader getFieldImpl(
const SchemaPool* pool, internal::StructReader reader,
schema::StructNode::Field::Reader field);
template <typename T>
friend struct internal::PointerHelpers;
friend class DynamicUnion::Reader;
friend class DynamicObject;
};
class DynamicStruct::Builder {
public:
Builder() = default;
template <typename T>
typename T::Builder to();
// Cast to a particular struct type.
schema::Node::Reader getSchemaNode();
schema::StructNode::Reader getSchema();
Maybe<schema::StructNode::Member::Reader> findMemberByName(Text::Reader name);
// Looks up the member with the given name, or returns nullptr if no such member exists.
DynamicValue::Builder getField(schema::StructNode::Field::Reader field);
// Returns the value of the given field.
void setField(schema::StructNode::Field::Reader field, DynamicValue::Reader value);
// Sets the value of the given field.
DynamicStruct::Builder initField(schema::StructNode::Field::Reader field);
DynamicList::Builder initField(schema::StructNode::Field::Reader field, uint size);
// Initialize a struct or list field by field schema.
DynamicUnion::Builder getUnion(schema::StructNode::Union::Reader un);
// Returns the value of the given union.
void copyFrom(Reader other);
Reader asReader();
private:
const SchemaPool* pool;
schema::Node::Reader schema;
internal::StructBuilder builder;
inline Builder(const SchemaPool* pool, schema::Node::Reader schema,
internal::StructBuilder builder)
: pool(pool), schema(schema), builder(builder) {}
static DynamicValue::Builder getFieldImpl(
const SchemaPool* pool, internal::StructBuilder builder,
schema::StructNode::Field::Reader field);
static void setFieldImpl(
const SchemaPool* pool, internal::StructBuilder builder,
schema::StructNode::Field::Reader field, DynamicValue::Reader value);
static DynamicList::Builder initListFieldImpl(
const SchemaPool* pool, internal::StructBuilder builder,
schema::StructNode::Field::Reader field, uint length);
static DynamicStruct::Builder initStructFieldImpl(
const SchemaPool* pool, internal::StructBuilder builder,
schema::StructNode::Field::Reader field);
template <typename T>
friend struct internal::PointerHelpers;
friend class DynamicUnion::Builder;
friend class DynamicObject;
};
// -------------------------------------------------------------------
class DynamicList::Reader {
public:
Reader() = default;
inline explicit Reader(internal::ListReader reader): reader(reader) {}
template <typename T>
typename T::Reader to();
// 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 uint size() { return reader.size() / ELEMENTS; }
DynamicValue::Reader operator[](uint index);
typedef internal::IndexingIterator<Reader, DynamicValue::Reader> iterator;
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
private:
const SchemaPool* pool;
// We don't encode the element type as schema::Type::Reader because we want to be able to
// construct DynamicList from List<T> without knowing of any particular field that has type
// List<T>, and we don't want to construct a fresh schema object every time this happens.
schema::Type::Body::Which elementType; // cannot be list
uint depth;
// Number of types elementType must be wrapped in List() to get the actual element type, e.g.
// List(List(List(Bool))) has depth = 2.
schema::Node::Body::Reader elementSchema; // if elementType is struct/enum/interface
internal::ListReader reader;
Reader(const SchemaPool* pool, schema::Type::Reader elementType, internal::ListReader reader);
Reader(const SchemaPool* pool, internal::ListSchema schema, internal::ListReader reader);
friend class DynamicStruct;
friend class DynamicObject;
};
class DynamicList::Builder {
public:
Builder() = default;
inline explicit Builder(internal::ListBuilder builder): builder(builder) {}
template <typename T>
typename T::Builder to();
// 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 uint size() { return builder.size() / ELEMENTS; }
DynamicStruct::Builder operator[](uint index);
typedef internal::IndexingIterator<Builder, DynamicStruct::Builder> iterator;
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
void copyFrom(Reader other);
Reader asReader();
private:
const SchemaPool* pool;
schema::Type::Body::Which elementType;
uint depth;
schema::Node::Body::Reader elementSchema;
internal::ListBuilder builder;
Builder(const SchemaPool* pool, schema::Type::Reader elementType, internal::ListBuilder builder);
Builder(const SchemaPool* pool, internal::ListSchema schema, internal::ListBuilder builder);
friend class DynamicStruct;
friend class DynamicObject;
};
// -------------------------------------------------------------------
namespace internal {
// Make sure ReaderFor<T> and BuilderFor<T> work for DynamicEnum, DynamicObject, DynamicStruct, and
// DynamicList, so that we can define DynamicValue::to().
template <>
struct MaybeReaderBuilder<DynamicEnum, Kind::UNKNOWN> {
typedef DynamicEnum Reader;
typedef DynamicEnum Builder;
};
template <>
struct MaybeReaderBuilder<DynamicObject, Kind::UNKNOWN> {
typedef DynamicObject::Reader Reader;
typedef DynamicObject::Builder Builder;
};
template <>
struct MaybeReaderBuilder<DynamicStruct, Kind::UNKNOWN> {
typedef DynamicStruct::Reader Reader;
typedef DynamicStruct::Builder Builder;
};
template <>
struct MaybeReaderBuilder<DynamicList, Kind::UNKNOWN> {
typedef DynamicList::Reader Reader;
typedef DynamicList::Builder Builder;
};
} // namespace internal
class DynamicValue::Reader {
public:
inline Reader() {}
inline Reader(Void voidValue);
inline Reader(bool boolValue);
inline Reader(int8_t int8Value);
inline Reader(int16_t int16Value);
inline Reader(int32_t int32Value);
inline Reader(int64_t int64Value);
inline Reader(uint8_t uint8Value);
inline Reader(uint16_t uint16Value);
inline Reader(uint32_t uint32Value);
inline Reader(uint64_t uint64Value);
inline Reader(float float32Value);
inline Reader(double float64Value);
inline Reader(Text::Reader textValue);
inline Reader(Data::Reader dataValue);
inline Reader(DynamicList::Reader listValue);
inline Reader(DynamicEnum enumValue);
inline Reader(DynamicStruct::Reader structValue);
inline Reader(DynamicObject::Reader objectValue);
template <typename T>
inline ReaderFor<T> to() { return ToImpl<T>::apply(*this); }
// Use to interpret the value as some type. Allowed types are:
// - Void, bool, [u]int{8,16,32,64}_t, float, double, any enum: Returns the raw value.
// - Text, Data, any struct type: Returns the corresponding Reader.
// - List<T> for any T listed above: Returns List<T>::Reader.
// - DynamicEnum: Returns DynamicEnum.
// - DynamicStruct, DynamicList, DynamicObject: Returns the corresponding Reader.
// If the requested type does not match the underlying data, the result is unspecified: it may
// throw an exception, it may return a garbage value, or it may return a composite value that
// when traversed throws exceptions or returns garbage values. Under none of these
// circumstances will the program crash.
inline schema::Type::Body::Which getType() { return type; }
// Get the type of this value.
private:
schema::Type::Body::Which type;
union {
Void voidValue;
bool boolValue;
int8_t int8Value;
int16_t int16Value;
int32_t int32Value;
int64_t int64Value;
uint8_t uint8Value;
uint16_t uint16Value;
uint32_t uint32Value;
uint64_t uint64Value;
float float32Value;
double float64Value;
Text::Reader textValue;
Data::Reader dataValue;
DynamicList::Reader listValue;
DynamicEnum enumValue;
DynamicStruct::Reader structValue;
DynamicObject::Reader objectValue;
};
template <typename T, Kind kind = kind<T>()> struct ToImpl;
// Implementation backing the to() method. Needs to be a struct to allow partial
// specialization. Has a method apply() which does the work.
};
class DynamicValue::Builder {
public:
inline Builder() {}
inline Builder(Void voidValue);
inline Builder(bool boolValue);
inline Builder(int8_t int8Value);
inline Builder(int16_t int16Value);
inline Builder(int32_t int32Value);
inline Builder(int64_t int64Value);
inline Builder(uint8_t uint8Value);
inline Builder(uint16_t uint16Value);
inline Builder(uint32_t uint32Value);
inline Builder(uint64_t uint64Value);
inline Builder(float float32Value);
inline Builder(double float64Value);
inline Builder(Text::Builder textValue);
inline Builder(Data::Builder dataValue);
inline Builder(DynamicList::Builder listValue);
inline Builder(DynamicEnum enumValue);
inline Builder(DynamicStruct::Builder structValue);
inline Builder(DynamicObject::Builder objectValue);
template <typename T>
inline BuilderFor<T> to() { return ToImpl<T>::apply(*this); }
// See DynamicValue::Reader::to().
inline schema::Type::Body::Which getType() { return type; }
// Get the type of this value.
Reader asReader();
private:
schema::Type::Body::Which type;
union {
Void voidValue;
bool boolValue;
int8_t int8Value;
int16_t int16Value;
int32_t int32Value;
int64_t int64Value;
uint8_t uint8Value;
uint16_t uint16Value;
uint32_t uint32Value;
uint64_t uint64Value;
float float32Value;
double float64Value;
Text::Builder textValue;
Data::Builder dataValue;
DynamicList::Builder listValue;
DynamicEnum enumValue;
DynamicStruct::Builder structValue;
DynamicObject::Builder objectValue;
};
template <typename T, Kind kind = kind<T>()> struct ToImpl;
// Implementation backing the to() method. Needs to be a struct to allow partial
// specialization. Has a method apply() which does the work.
};
// =======================================================================================
// Implementation details.
#define CAPNPROTO_DECLARE_TYPE(name, discrim, typeName) \
inline DynamicValue::Reader::Reader(ReaderFor<typeName> name##Value) \
: type(schema::Type::Body::discrim##_TYPE), name##Value(name##Value) {} \
inline DynamicValue::Builder::Builder(BuilderFor<typeName> name##Value) \
: type(schema::Type::Body::discrim##_TYPE), name##Value(name##Value) {} \
template <> \
struct DynamicValue::Reader::ToImpl<typeName> { \
static ReaderFor<typeName> apply(Reader reader); \
}; \
template <> \
struct DynamicValue::Builder::ToImpl<typeName> { \
static BuilderFor<typeName> apply(Builder builder); \
};
//CAPNPROTO_DECLARE_TYPE(void, VOID, Void)
CAPNPROTO_DECLARE_TYPE(bool, BOOL, bool)
CAPNPROTO_DECLARE_TYPE(int8, INT8, int8_t)
CAPNPROTO_DECLARE_TYPE(int16, INT16, int16_t)
CAPNPROTO_DECLARE_TYPE(int32, INT32, int32_t)
CAPNPROTO_DECLARE_TYPE(int64, INT64, int64_t)
CAPNPROTO_DECLARE_TYPE(uint8, UINT8, uint8_t)
CAPNPROTO_DECLARE_TYPE(uint16, UINT16, uint16_t)
CAPNPROTO_DECLARE_TYPE(uint32, UINT32, uint32_t)
CAPNPROTO_DECLARE_TYPE(uint64, UINT64, uint64_t)
CAPNPROTO_DECLARE_TYPE(float32, FLOAT32, float)
CAPNPROTO_DECLARE_TYPE(float64, FLOAT64, double)
CAPNPROTO_DECLARE_TYPE(text, TEXT, Text)
CAPNPROTO_DECLARE_TYPE(data, DATA, Data)
CAPNPROTO_DECLARE_TYPE(list, LIST, DynamicList)
CAPNPROTO_DECLARE_TYPE(struct, STRUCT, DynamicStruct)
CAPNPROTO_DECLARE_TYPE(enum, ENUM, DynamicEnum)
CAPNPROTO_DECLARE_TYPE(object, OBJECT, DynamicObject)
#undef CAPNPROTO_DECLARE_TYPE
// CAPNPROTO_DECLARE_TYPE(Void) causes gcc 4.7 to segfault. If I do it manually and remove the
// ReaderFor<> and BuilderFor<> wrappers, it works.
inline DynamicValue::Reader::Reader(Void voidValue) \
: type(schema::Type::Body::VOID_TYPE), voidValue(voidValue) {} \
inline DynamicValue::Builder::Builder(Void voidValue) \
: type(schema::Type::Body::VOID_TYPE), voidValue(voidValue) {} \
template <>
struct DynamicValue::Reader::ToImpl<Void> {
static Void apply(Reader reader);
};
template <>
struct DynamicValue::Builder::ToImpl<Void> {
static Void apply(Builder builder);
};
template <typename T>
struct DynamicValue::Reader::ToImpl<T, Kind::ENUM> {
static T apply(Reader reader) {
return reader.to<DynamicEnum>().to<T>();
}
};
template <typename T>
struct DynamicValue::Builder::ToImpl<T, Kind::ENUM> {
static T apply(Builder builder) {
return builder.to<DynamicEnum>().to<T>();
}
};
template <typename T>
struct DynamicValue::Reader::ToImpl<T, Kind::STRUCT> {
static T apply(Reader reader) {
return reader.to<DynamicStruct>().to<T>();
}
};
template <typename T>
struct DynamicValue::Builder::ToImpl<T, Kind::STRUCT> {
static T apply(Builder builder) {
return builder.to<DynamicStruct>().to<T>();
}
};
template <typename T>
struct DynamicValue::Reader::ToImpl<T, Kind::LIST> {
static T apply(Reader reader) {
return reader.to<DynamicList>().to<T>();
}
};
template <typename T>
struct DynamicValue::Builder::ToImpl<T, Kind::LIST> {
static T apply(Builder builder) {
return builder.to<DynamicList>().to<T>();
}
};
// -------------------------------------------------------------------
template <typename T>
struct DynamicObject::Reader::ToImpl<T, Kind::STRUCT> {
static T apply(Reader reader) {
return reader.toStruct(typeId<T>()).to<T>();
}
};
template <typename T>
struct DynamicObject::Builder::ToImpl<T, Kind::STRUCT> {
static T apply(Builder builder) {
return builder.toStruct(typeId<T>()).to<T>();
}
};
template <typename T>
struct DynamicObject::Reader::ToImpl<List<T>, Kind::LIST> {
static T apply(Reader reader) {
return reader.toList(internal::ListSchemaFor<T>::schema).to<T>();
}
};
template <typename T>
struct DynamicObject::Builder::ToImpl<List<T>, Kind::LIST> {
static T apply(Builder builder) {
return builder.toList(internal::ListSchemaFor<T>::schema).to<T>();
}
};
} // namespace capnproto
#endif // CAPNPROTO_DYNAMIC_H_
......@@ -33,6 +33,11 @@ namespace capnproto {
class DynamicStruct; // So that it can be declared a friend.
template <typename T>
inline constexpr uint64_t typeId();
// typeId<MyType>() returns the type ID as defined in the schema. Works with structs, enums, and
// interfaces.
namespace internal {
template <typename T>
......@@ -118,18 +123,26 @@ struct PointerHelpers<TrustedMessage> {
#endif
struct RawSchema {
const word* encodedNode;
const RawSchema* dependencies;
};
template <typename T>
inline constexpr const RawSchema& rawSchema();
} // namespace internal
} // namespace capnproto
#define CAPNPROTO_DECLARE_ENUM(type) \
#define CAPNPROTO_DECLARE_ENUM(type, id) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::ENUM; }
#define CAPNPROTO_DECLARE_STRUCT(type, dataWordSize, pointerCount, preferredElementEncoding) \
#define CAPNPROTO_DECLARE_STRUCT(type, id, dataWordSize, pointerCount, preferredElementEncoding) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::STRUCT; }; \
template <> struct StructSizeFor<type> { \
static constexpr StructSize value = StructSize( \
dataWordSize * WORDS, pointerCount * REFERENCES, FieldSize::preferredElementEncoding); \
}
#define CAPNPROTO_DECLARE_INTERFACE(type) \
#define CAPNPROTO_DECLARE_INTERFACE(type, id) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::INTERFACE; }
#endif // CAPNPROTO_GENERATED_HEADER_SUPPORT_H_
......@@ -265,6 +265,7 @@ enumerantContext parent desc = mkStrContext context where
enumContext parent desc = mkStrContext context where
context "enumName" = MuVariable $ enumName desc
context "enumId" = MuVariable (printf "0x%16x" (enumId desc) ::String)
context "enumerants" = MuList $ map (enumerantContext context) $ enumerants desc
context s = parent s
......@@ -383,6 +384,7 @@ structContext parent desc = mkStrContext context where
context "typeFields" = context "structFields"
context "structName" = MuVariable $ structName desc
context "structId" = MuVariable (printf "0x%16x" (structId desc) ::String)
context "structFullName" = MuVariable $ fullName (DescStruct desc)
context "structFields" = MuList $ map (fieldContext context) $ structFields desc
context "structUnions" = MuList $ map (unionContext context) $ structUnions desc
......
......@@ -105,16 +105,18 @@ namespace internal {
{{#typeStructOrUnion}}
{{#typeStruct}}
CAPNPROTO_DECLARE_STRUCT(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}},
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}, {{structId}},
{{structDataSize}}, {{structReferenceCount}}, {{structPreferredListEncoding}});
{{#structNestedEnums}}
CAPNPROTO_DECLARE_ENUM(::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}});
CAPNPROTO_DECLARE_ENUM(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}}, {{enumId}});
{{/structNestedEnums}}
{{/typeStruct}}
{{/typeStructOrUnion}}
{{/fileTypes}}
{{#fileEnums}}
CAPNPROTO_DECLARE_ENUM(::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{enumName}});
CAPNPROTO_DECLARE_ENUM(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{enumName}}, {{enumId}});
{{/fileEnums}}
} // namespace capnproto
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment