Commit bdb86c21 authored by Kenton Varda's avatar Kenton Varda

Move Maybe into common.h and improve its interface.

parent 328c9a83
...@@ -228,6 +228,13 @@ TEST(DynamicApi, DynamicGenericObjects) { ...@@ -228,6 +228,13 @@ TEST(DynamicApi, DynamicGenericObjects) {
} }
} }
#define EXPECT_MAYBE_EQ(name, exp, expected, actual) \
KJ_IF_MAYBE(name, exp) { \
EXPECT_EQ(expected, actual); \
} else { \
FAIL() << "Maybe was empty."; \
}
TEST(DynamicApi, UnionsRead) { TEST(DynamicApi, UnionsRead) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.initRoot<TestUnion>(); auto root = builder.initRoot<TestUnion>();
...@@ -241,26 +248,22 @@ TEST(DynamicApi, UnionsRead) { ...@@ -241,26 +248,22 @@ TEST(DynamicApi, UnionsRead) {
auto dynamic = toDynamic(root.asReader()); auto dynamic = toDynamic(root.asReader());
{ {
auto u = dynamic.get("union0").as<DynamicUnion>(); auto u = dynamic.get("union0").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u0f1s32", w->getProto().getName());
EXPECT_EQ("u0f1s32", u.which()->getProto().getName());
EXPECT_EQ(1234567, u.get().as<int32_t>()); EXPECT_EQ(1234567, u.get().as<int32_t>());
} }
{ {
auto u = dynamic.get("union1").as<DynamicUnion>(); auto u = dynamic.get("union1").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u1f1sp", w->getProto().getName());
EXPECT_EQ("u1f1sp", u.which()->getProto().getName());
EXPECT_EQ("foo", u.get().as<Text>()); EXPECT_EQ("foo", u.get().as<Text>());
} }
{ {
auto u = dynamic.get("union2").as<DynamicUnion>(); auto u = dynamic.get("union2").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u2f0s1", w->getProto().getName());
EXPECT_EQ("u2f0s1", u.which()->getProto().getName());
EXPECT_TRUE(u.get().as<bool>()); EXPECT_TRUE(u.get().as<bool>());
} }
{ {
auto u = dynamic.get("union3").as<DynamicUnion>(); auto u = dynamic.get("union3").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u3f0s64", w->getProto().getName());
EXPECT_EQ("u3f0s64", u.which()->getProto().getName());
EXPECT_EQ(1234567890123456789ll, u.get().as<int64_t>()); EXPECT_EQ(1234567890123456789ll, u.get().as<int64_t>());
} }
} }
...@@ -270,26 +273,22 @@ TEST(DynamicApi, UnionsRead) { ...@@ -270,26 +273,22 @@ TEST(DynamicApi, UnionsRead) {
auto dynamic = toDynamic(root); auto dynamic = toDynamic(root);
{ {
auto u = dynamic.get("union0").as<DynamicUnion>(); auto u = dynamic.get("union0").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u0f1s32", w->getProto().getName());
EXPECT_EQ("u0f1s32", u.which()->getProto().getName());
EXPECT_EQ(1234567, u.get().as<int32_t>()); EXPECT_EQ(1234567, u.get().as<int32_t>());
} }
{ {
auto u = dynamic.get("union1").as<DynamicUnion>(); auto u = dynamic.get("union1").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u1f1sp", w->getProto().getName());
EXPECT_EQ("u1f1sp", u.which()->getProto().getName());
EXPECT_EQ("foo", u.get().as<Text>()); EXPECT_EQ("foo", u.get().as<Text>());
} }
{ {
auto u = dynamic.get("union2").as<DynamicUnion>(); auto u = dynamic.get("union2").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u2f0s1", w->getProto().getName());
EXPECT_EQ("u2f0s1", u.which()->getProto().getName());
EXPECT_TRUE(u.get().as<bool>()); EXPECT_TRUE(u.get().as<bool>());
} }
{ {
auto u = dynamic.get("union3").as<DynamicUnion>(); auto u = dynamic.get("union3").as<DynamicUnion>();
ASSERT_TRUE(u.which() != nullptr); EXPECT_MAYBE_EQ(w, u.which(), "u3f0s64", w->getProto().getName());
EXPECT_EQ("u3f0s64", u.which()->getProto().getName());
EXPECT_EQ(1234567890123456789ll, u.get().as<int64_t>()); EXPECT_EQ(1234567890123456789ll, u.get().as<int64_t>());
} }
} }
......
...@@ -169,20 +169,18 @@ kj::Maybe<StructSchema::Member> DynamicUnion::Builder::which() { ...@@ -169,20 +169,18 @@ kj::Maybe<StructSchema::Member> DynamicUnion::Builder::which() {
} }
DynamicValue::Reader DynamicUnion::Reader::get() { DynamicValue::Reader DynamicUnion::Reader::get() {
auto w = which(); KJ_IF_MAYBE(w, which()) {
if (w == nullptr) {
return nullptr;
} else {
return DynamicValue::Reader(DynamicStruct::Reader::getImpl(reader, *w)); return DynamicValue::Reader(DynamicStruct::Reader::getImpl(reader, *w));
} else {
return nullptr;
} }
} }
DynamicValue::Builder DynamicUnion::Builder::get() { DynamicValue::Builder DynamicUnion::Builder::get() {
auto w = which(); KJ_IF_MAYBE(w, which()) {
if (w == nullptr) {
return nullptr;
} else {
return DynamicValue::Builder(DynamicStruct::Builder::getImpl(builder, *w)); return DynamicValue::Builder(DynamicStruct::Builder::getImpl(builder, *w));
} else {
return nullptr;
} }
} }
...@@ -256,22 +254,26 @@ Data::Builder DynamicUnion::Builder::initObjectAsData(Text::Reader name, uint si ...@@ -256,22 +254,26 @@ Data::Builder DynamicUnion::Builder::initObjectAsData(Text::Reader name, uint si
} }
StructSchema::Member DynamicUnion::Builder::checkIsObject() { StructSchema::Member DynamicUnion::Builder::checkIsObject() {
auto w = which(); KJ_IF_MAYBE(w, which()) {
PRECOND(w != nullptr, "Can't get() unknown union value.");
CHECK(w->getProto().getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER, CHECK(w->getProto().getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Unsupported union member type."); "Unsupported union member type.");
PRECOND(w->getProto().getBody().getFieldMember().getType().getBody().which() == PRECOND(w->getProto().getBody().getFieldMember().getType().getBody().which() ==
schema::Type::Body::OBJECT_TYPE, "Expected Object."); schema::Type::Body::OBJECT_TYPE, "Expected Object.");
return *w; return *w;
} else {
FAIL_PRECOND("Can't get() unknown union value.");
}
} }
void DynamicUnion::Builder::setDiscriminant(StructSchema::Member member) { void DynamicUnion::Builder::setDiscriminant(StructSchema::Member member) {
auto containingUnion = member.getContainingUnion(); KJ_IF_MAYBE(containingUnion, member.getContainingUnion()) {
PRECOND(containingUnion != nullptr && *containingUnion == schema, PRECOND(*containingUnion == schema, "`member` is not a member of this union.");
"`member` is not a member of this union.");
builder.setDataField<uint16_t>( builder.setDataField<uint16_t>(
schema.getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS, schema.getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS,
member.getIndex()); member.getIndex());
} else {
FAIL_PRECOND("`member` is not a member of this union.");
}
} }
void DynamicUnion::Builder::setObjectDiscriminant(StructSchema::Member member) { void DynamicUnion::Builder::setObjectDiscriminant(StructSchema::Member member) {
...@@ -874,16 +876,17 @@ void DynamicStruct::Builder::setImpl( ...@@ -874,16 +876,17 @@ void DynamicStruct::Builder::setImpl(
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: { case schema::StructNode::Member::Body::UNION_MEMBER: {
auto src = value.as<DynamicUnion>(); auto src = value.as<DynamicUnion>();
auto which = src.which(); KJ_IF_MAYBE(which, src.which()) {
RECOVERABLE_PRECOND(which != nullptr, getImpl(builder, member).as<DynamicUnion>().set(member, src.get());
return;
} else {
FAIL_RECOVERABLE_PRECOND(
"Trying to copy a union value, but the union's discriminant is not recognized. It " "Trying to copy a union value, but the union's discriminant is not recognized. It "
"was probably constructed using a newer version of the schema.") { "was probably constructed using a newer version of the schema.") {
// Just don't copy anything. // Just don't copy anything.
return; return;
} }
}
getImpl(builder, member).as<DynamicUnion>().set(member, src.get());
return;
} }
case schema::StructNode::Member::Body::FIELD_MEMBER: { case schema::StructNode::Member::Body::FIELD_MEMBER: {
......
...@@ -76,10 +76,9 @@ TEST(Schema, Structs) { ...@@ -76,10 +76,9 @@ TEST(Schema, Structs) {
EXPECT_ANY_THROW(member.asUnion()); EXPECT_ANY_THROW(member.asUnion());
kj::Maybe<StructSchema::Member> lookup = schema.findMemberByName("voidField"); StructSchema::Member lookup = schema.getMemberByName("voidField");
ASSERT_TRUE(lookup != nullptr); EXPECT_TRUE(lookup == member);
EXPECT_TRUE(*lookup == member); EXPECT_TRUE(lookup != schema.getMembers()[1]);
EXPECT_TRUE(*lookup != schema.getMembers()[1]);
EXPECT_TRUE(schema.findMemberByName("noSuchField") == nullptr); EXPECT_TRUE(schema.findMemberByName("noSuchField") == nullptr);
...@@ -105,15 +104,15 @@ TEST(Schema, FieldLookupOutOfOrder) { ...@@ -105,15 +104,15 @@ TEST(Schema, FieldLookupOutOfOrder) {
EXPECT_EQ("garply", schema.getMembers()[7].getProto().getName()); EXPECT_EQ("garply", schema.getMembers()[7].getProto().getName());
EXPECT_EQ("baz", schema.getMembers()[8].getProto().getName()); EXPECT_EQ("baz", schema.getMembers()[8].getProto().getName());
EXPECT_EQ(3, schema.findMemberByName("foo")->getProto().getOrdinal()); EXPECT_EQ(3, schema.getMemberByName("foo").getProto().getOrdinal());
EXPECT_EQ(2, schema.findMemberByName("bar")->getProto().getOrdinal()); EXPECT_EQ(2, schema.getMemberByName("bar").getProto().getOrdinal());
EXPECT_EQ(8, schema.findMemberByName("baz")->getProto().getOrdinal()); EXPECT_EQ(8, schema.getMemberByName("baz").getProto().getOrdinal());
EXPECT_EQ(0, schema.findMemberByName("qux")->getProto().getOrdinal()); EXPECT_EQ(0, schema.getMemberByName("qux").getProto().getOrdinal());
EXPECT_EQ(6, schema.findMemberByName("quux")->getProto().getOrdinal()); EXPECT_EQ(6, schema.getMemberByName("quux").getProto().getOrdinal());
EXPECT_EQ(4, schema.findMemberByName("corge")->getProto().getOrdinal()); EXPECT_EQ(4, schema.getMemberByName("corge").getProto().getOrdinal());
EXPECT_EQ(1, schema.findMemberByName("grault")->getProto().getOrdinal()); EXPECT_EQ(1, schema.getMemberByName("grault").getProto().getOrdinal());
EXPECT_EQ(7, schema.findMemberByName("garply")->getProto().getOrdinal()); EXPECT_EQ(7, schema.getMemberByName("garply").getProto().getOrdinal());
EXPECT_EQ(5, schema.findMemberByName("waldo")->getProto().getOrdinal()); EXPECT_EQ(5, schema.getMemberByName("waldo").getProto().getOrdinal());
} }
TEST(Schema, Unions) { TEST(Schema, Unions) {
...@@ -122,21 +121,18 @@ TEST(Schema, Unions) { ...@@ -122,21 +121,18 @@ TEST(Schema, Unions) {
EXPECT_TRUE(schema.findMemberByName("bit0") != nullptr); EXPECT_TRUE(schema.findMemberByName("bit0") != nullptr);
EXPECT_TRUE(schema.findMemberByName("u1f0s8") == nullptr); EXPECT_TRUE(schema.findMemberByName("u1f0s8") == nullptr);
auto maybeUnion1 = schema.findMemberByName("union1"); auto union1 = schema.getMemberByName("union1").asUnion();
ASSERT_TRUE(maybeUnion1 != nullptr);
auto union1 = maybeUnion1->asUnion();
EXPECT_TRUE(union1.findMemberByName("bin0") == nullptr); EXPECT_TRUE(union1.findMemberByName("bin0") == nullptr);
EXPECT_TRUE(union1.getContainingUnion() == nullptr); EXPECT_TRUE(union1.getContainingUnion() == nullptr);
auto maybeU1f0s8 = union1.findMemberByName("u1f0s8"); auto u1f0s8 = union1.getMemberByName("u1f0s8");
ASSERT_TRUE(maybeU1f0s8 != nullptr);
auto u1f0s8 = *maybeU1f0s8;
EXPECT_EQ("u1f0s8", u1f0s8.getProto().getName()); EXPECT_EQ("u1f0s8", u1f0s8.getProto().getName());
EXPECT_TRUE(u1f0s8.getContainingStruct() == schema); EXPECT_TRUE(u1f0s8.getContainingStruct() == schema);
EXPECT_TRUE(*u1f0s8.getContainingUnion() == union1); KJ_IF_MAYBE(containing, u1f0s8.getContainingUnion()) {
EXPECT_TRUE(*containing == union1);
} else {
ADD_FAILURE() << "u1f0s8.getContainingUnion() returned null";
}
EXPECT_TRUE(union1.findMemberByName("u1f1s8") != nullptr); EXPECT_TRUE(union1.findMemberByName("u1f1s8") != nullptr);
EXPECT_TRUE(union1.findMemberByName("u1f0s32") != nullptr); EXPECT_TRUE(union1.findMemberByName("u1f0s32") != nullptr);
...@@ -166,10 +162,9 @@ TEST(Schema, Enums) { ...@@ -166,10 +162,9 @@ TEST(Schema, Enums) {
EXPECT_EQ("foo", enumerant.getProto().getName()); EXPECT_EQ("foo", enumerant.getProto().getName());
EXPECT_TRUE(enumerant.getContainingEnum() == schema); EXPECT_TRUE(enumerant.getContainingEnum() == schema);
kj::Maybe<EnumSchema::Enumerant> lookup = schema.findEnumerantByName("foo"); EnumSchema::Enumerant lookup = schema.getEnumerantByName("foo");
ASSERT_TRUE(lookup != nullptr); EXPECT_TRUE(lookup == enumerant);
EXPECT_TRUE(*lookup == enumerant); EXPECT_TRUE(lookup != schema.getEnumerants()[1]);
EXPECT_TRUE(*lookup != schema.getEnumerants()[1]);
EXPECT_TRUE(schema.findEnumerantByName("noSuchEnumerant") == nullptr); EXPECT_TRUE(schema.findEnumerantByName("noSuchEnumerant") == nullptr);
...@@ -259,8 +254,7 @@ TEST(Schema, Lists) { ...@@ -259,8 +254,7 @@ TEST(Schema, Lists) {
{ {
auto context = Schema::from<TestAllTypes>(); auto context = Schema::from<TestAllTypes>();
auto type = context.findMemberByName("enumList") auto type = context.getMemberByName("enumList").getProto().getBody().getFieldMember().getType();
->getProto().getBody().getFieldMember().getType();
ListSchema schema = ListSchema::of(type.getBody().getListType(), context); ListSchema schema = ListSchema::of(type.getBody().getListType(), context);
EXPECT_EQ(schema::Type::Body::ENUM_TYPE, schema.whichElementType()); EXPECT_EQ(schema::Type::Body::ENUM_TYPE, schema.whichElementType());
......
...@@ -129,9 +129,11 @@ kj::Maybe<StructSchema::Member> StructSchema::findMemberByName(Text::Reader name ...@@ -129,9 +129,11 @@ kj::Maybe<StructSchema::Member> StructSchema::findMemberByName(Text::Reader name
} }
StructSchema::Member StructSchema::getMemberByName(Text::Reader name) const { StructSchema::Member StructSchema::getMemberByName(Text::Reader name) const {
kj::Maybe<StructSchema::Member> member = findMemberByName(name); KJ_IF_MAYBE(member, findMemberByName(name)) {
PRECOND(member != nullptr, "struct has no such member", name);
return *member; return *member;
} else {
FAIL_PRECOND("struct has no such member", name);
}
} }
kj::Maybe<StructSchema::Union> StructSchema::Member::getContainingUnion() const { kj::Maybe<StructSchema::Union> StructSchema::Member::getContainingUnion() const {
...@@ -155,9 +157,11 @@ kj::Maybe<StructSchema::Member> StructSchema::Union::findMemberByName(Text::Read ...@@ -155,9 +157,11 @@ kj::Maybe<StructSchema::Member> StructSchema::Union::findMemberByName(Text::Read
} }
StructSchema::Member StructSchema::Union::getMemberByName(Text::Reader name) const { StructSchema::Member StructSchema::Union::getMemberByName(Text::Reader name) const {
kj::Maybe<StructSchema::Member> member = findMemberByName(name); KJ_IF_MAYBE(member, findMemberByName(name)) {
PRECOND(member != nullptr, "union has no such member", name);
return *member; return *member;
} else {
FAIL_PRECOND("union has no such member", name);
}
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -171,9 +175,11 @@ kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(Text::Reader na ...@@ -171,9 +175,11 @@ kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(Text::Reader na
} }
EnumSchema::Enumerant EnumSchema::getEnumerantByName(Text::Reader name) const { EnumSchema::Enumerant EnumSchema::getEnumerantByName(Text::Reader name) const {
kj::Maybe<EnumSchema::Enumerant> enumerant = findEnumerantByName(name); KJ_IF_MAYBE(enumerant, findEnumerantByName(name)) {
PRECOND(enumerant != nullptr, "enum has no such enumerant", name);
return *enumerant; return *enumerant;
} else {
FAIL_PRECOND("enum has no such enumerant", name);
}
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -187,9 +193,11 @@ kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(Text::Reade ...@@ -187,9 +193,11 @@ kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(Text::Reade
} }
InterfaceSchema::Method InterfaceSchema::getMethodByName(Text::Reader name) const { InterfaceSchema::Method InterfaceSchema::getMethodByName(Text::Reader name) const {
kj::Maybe<InterfaceSchema::Method> method = findMethodByName(name); KJ_IF_MAYBE(method, findMethodByName(name)) {
PRECOND(method != nullptr, "interface has no such method", name);
return *method; return *method;
} else {
FAIL_PRECOND("interface has no such method", name);
}
} }
// ======================================================================================= // =======================================================================================
......
...@@ -111,13 +111,11 @@ static void print(std::ostream& os, DynamicValue::Reader value, ...@@ -111,13 +111,11 @@ static void print(std::ostream& os, DynamicValue::Reader value,
} }
case DynamicValue::ENUM: { case DynamicValue::ENUM: {
auto enumValue = value.as<DynamicEnum>(); auto enumValue = value.as<DynamicEnum>();
kj::Maybe<EnumSchema::Enumerant> enumerant = KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) {
enumValue.getEnumerant(); os << enumerant->getProto().getName().c_str();
if (enumerant == nullptr) { } else {
// Unknown enum value; output raw number. // Unknown enum value; output raw number.
os << enumValue.getRaw(); os << enumValue.getRaw();
} else {
os << enumerant->getProto().getName().c_str();
} }
break; break;
} }
...@@ -151,16 +149,15 @@ static void print(std::ostream& os, DynamicValue::Reader value, ...@@ -151,16 +149,15 @@ static void print(std::ostream& os, DynamicValue::Reader value,
} }
case DynamicValue::UNION: { case DynamicValue::UNION: {
auto unionValue = value.as<DynamicUnion>(); auto unionValue = value.as<DynamicUnion>();
kj::Maybe<StructSchema::Member> tag = unionValue.which(); KJ_IF_MAYBE(tag, unionValue.which()) {
if (tag == nullptr) {
// Unknown union member; must have come from newer
// version of the protocol.
os << "?";
} else {
os << tag->getProto().getName() << "("; os << tag->getProto().getName() << "(";
print(os, unionValue.get(), print(os, unionValue.get(),
tag->getProto().getBody().getFieldMember().getType().getBody().which()); tag->getProto().getBody().getFieldMember().getType().getBody().which());
os << ")"; os << ")";
} else {
// Unknown union member; must have come from newer
// version of the protocol.
os << "?";
} }
break; break;
} }
......
...@@ -368,13 +368,20 @@ void checkList(T reader, std::initializer_list<ReaderFor<Element>> expected) { ...@@ -368,13 +368,20 @@ void checkList(T reader, std::initializer_list<ReaderFor<Element>> expected) {
} }
} }
Text::Reader name(DynamicEnum e) {
KJ_IF_MAYBE(schema, e.getEnumerant()) {
return schema->getProto().getName();
} else {
return "(unknown enumerant)";
}
}
template <typename T> template <typename T>
void checkEnumList(T reader, std::initializer_list<const char*> expected) { void checkEnumList(T reader, std::initializer_list<const char*> expected) {
auto list = reader.as<DynamicList>(); auto list = reader.as<DynamicList>();
ASSERT_EQ(expected.size(), list.size()); ASSERT_EQ(expected.size(), list.size());
for (uint i = 0; i < expected.size(); i++) { for (uint i = 0; i < expected.size(); i++) {
EXPECT_EQ(expected.begin()[i], EXPECT_EQ(expected.begin()[i], name(list[i].as<DynamicEnum>()));
list[i].as<DynamicEnum>().getEnumerant()->getProto().getName());
} }
} }
...@@ -416,8 +423,7 @@ void dynamicCheckTestMessage(Reader reader) { ...@@ -416,8 +423,7 @@ void dynamicCheckTestMessage(Reader reader) {
EXPECT_EQ("really nested", subSubReader.get("structField").as<DynamicStruct>() EXPECT_EQ("really nested", subSubReader.get("structField").as<DynamicStruct>()
.get("textField").as<Text>()); .get("textField").as<Text>());
} }
EXPECT_EQ("baz", EXPECT_EQ("baz", name(subReader.get("enumField").as<DynamicEnum>()));
subReader.get("enumField").as<DynamicEnum>().getEnumerant()->getProto().getName());
checkList<Void>(subReader.get("voidList"), {Void::VOID, Void::VOID, Void::VOID}); checkList<Void>(subReader.get("voidList"), {Void::VOID, Void::VOID, Void::VOID});
checkList<bool>(subReader.get("boolList"), {false, true, false, true, true}); checkList<bool>(subReader.get("boolList"), {false, true, false, true, true});
...@@ -443,8 +449,7 @@ void dynamicCheckTestMessage(Reader reader) { ...@@ -443,8 +449,7 @@ void dynamicCheckTestMessage(Reader reader) {
} }
checkEnumList(subReader.get("enumList"), {"qux", "bar", "grault"}); checkEnumList(subReader.get("enumList"), {"qux", "bar", "grault"});
} }
EXPECT_EQ("corge", EXPECT_EQ("corge", name(reader.get("enumField").as<DynamicEnum>()));
reader.get("enumField").as<DynamicEnum>().getEnumerant()->getProto().getName());
EXPECT_EQ(6u, reader.get("voidList").as<DynamicList>().size()); EXPECT_EQ(6u, reader.get("voidList").as<DynamicList>().size());
checkList<bool>(reader.get("boolList"), {true, false, false, true}); checkList<bool>(reader.get("boolList"), {true, false, false, true});
......
// 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.
#include "common.h"
#include <gtest/gtest.h>
namespace kj {
namespace {
TEST(Common, Maybe) {
{
Maybe<int> m = 123;
EXPECT_FALSE(m == nullptr);
EXPECT_TRUE(m != nullptr);
KJ_IF_MAYBE(v, m) {
EXPECT_EQ(123, *v);
} else {
ADD_FAILURE();
}
KJ_IF_MAYBE(v, mv(m)) {
EXPECT_EQ(123, *v);
} else {
ADD_FAILURE();
}
}
{
Maybe<int> m = nullptr;
EXPECT_TRUE(m == nullptr);
EXPECT_FALSE(m != nullptr);
KJ_IF_MAYBE(v, m) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
KJ_IF_MAYBE(v, mv(m)) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
}
int i = 234;
{
Maybe<int&> m = i;
EXPECT_FALSE(m == nullptr);
EXPECT_TRUE(m != nullptr);
KJ_IF_MAYBE(v, m) {
EXPECT_EQ(&i, v);
} else {
ADD_FAILURE();
}
KJ_IF_MAYBE(v, mv(m)) {
EXPECT_EQ(&i, v);
} else {
ADD_FAILURE();
}
}
{
Maybe<int&> m = nullptr;
EXPECT_TRUE(m == nullptr);
EXPECT_FALSE(m != nullptr);
KJ_IF_MAYBE(v, m) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
KJ_IF_MAYBE(v, mv(m)) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
}
{
Maybe<int&> m = &i;
EXPECT_FALSE(m == nullptr);
EXPECT_TRUE(m != nullptr);
KJ_IF_MAYBE(v, m) {
EXPECT_EQ(&i, v);
} else {
ADD_FAILURE();
}
KJ_IF_MAYBE(v, mv(m)) {
EXPECT_EQ(&i, v);
} else {
ADD_FAILURE();
}
}
{
Maybe<int&> m = upcast<int*>(nullptr);
EXPECT_TRUE(m == nullptr);
EXPECT_FALSE(m != nullptr);
KJ_IF_MAYBE(v, m) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
KJ_IF_MAYBE(v, mv(m)) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
}
{
Maybe<int> m = &i;
EXPECT_FALSE(m == nullptr);
EXPECT_TRUE(m != nullptr);
KJ_IF_MAYBE(v, m) {
EXPECT_NE(v, &i);
EXPECT_EQ(234, *v);
} else {
ADD_FAILURE();
}
KJ_IF_MAYBE(v, mv(m)) {
EXPECT_NE(v, &i);
EXPECT_EQ(234, *v);
} else {
ADD_FAILURE();
}
}
{
Maybe<int> m = upcast<int*>(nullptr);
EXPECT_TRUE(m == nullptr);
EXPECT_FALSE(m != nullptr);
KJ_IF_MAYBE(v, m) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
KJ_IF_MAYBE(v, mv(m)) {
ADD_FAILURE();
EXPECT_EQ(0, *v); // avoid unused warning
}
}
}
class Foo {
public:
virtual ~Foo() {}
};
class Bar: public Foo {
public:
virtual ~Bar() {}
};
class Baz: public Foo {
public:
virtual ~Baz() {}
};
TEST(Common, Downcast) {
Bar bar;
Foo& foo = bar;
EXPECT_EQ(&bar, &downcast<Bar&>(foo));
EXPECT_EQ(&bar, downcast<Bar*>(&foo));
EXPECT_ANY_THROW(downcast<Baz&>(foo));
EXPECT_ANY_THROW(downcast<Baz*>(&foo));
#if KJ_NO_RTTI
EXPECT_TRUE(dynamicDowncastIfAvailable<Bar&>(foo) == nullptr);
EXPECT_TRUE(dynamicDowncastIfAvailable<Baz&>(foo) == nullptr);
#else
KJ_IF_MAYBE(m, dynamicDowncastIfAvailable<Bar&>(foo)) {
EXPECT_EQ(&bar, m);
} else {
ADD_FAILURE() << "Dynamic downcast returned null.";
}
EXPECT_TRUE(dynamicDowncastIfAvailable<Baz&>(foo) == nullptr);
#endif
}
} // namespace
} // namespace kj
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
// //
// This defines very simple utilities that are widely applicable. // This defines very simple utilities that are widely applicable.
#include <stddef.h>
#ifndef KJ_COMMON_H_ #ifndef KJ_COMMON_H_
#define KJ_COMMON_H_ #define KJ_COMMON_H_
...@@ -58,6 +60,14 @@ typedef unsigned char byte; ...@@ -58,6 +60,14 @@ typedef unsigned char byte;
// ======================================================================================= // =======================================================================================
// Common macros, especially for common yet compiler-specific features. // Common macros, especially for common yet compiler-specific features.
#ifndef KJ_NO_RTTI
#ifdef __GNUC__
#if !__GXX_RTTI
#define KJ_NO_RTTI
#endif
#endif
#endif
#define KJ_DISALLOW_COPY(classname) \ #define KJ_DISALLOW_COPY(classname) \
classname(const classname&) = delete; \ classname(const classname&) = delete; \
classname& operator=(const classname&) = delete classname& operator=(const classname&) = delete
...@@ -183,6 +193,263 @@ template<typename T> constexpr T&& fwd(RemoveReference<T>&& t) noexcept { ...@@ -183,6 +193,263 @@ template<typename T> constexpr T&& fwd(RemoveReference<T>&& t) noexcept {
return static_cast<T&&>(t); return static_cast<T&&>(t);
} }
// =======================================================================================
// Manually invoking constructors and destructors
//
// ctor(x, ...) and dtor(x) invoke x's constructor or destructor, respectively.
// We want placement new, but we don't want to #include <new>. operator new cannot be defined in
// a namespace, and defining it globally conflicts with the definition in <new>. So we have to
// define a dummy type and an operator new that uses it.
namespace internal {
struct PlacementNew {};
} // namespace internal
} // namespace kj
inline void* operator new(size_t, kj::internal::PlacementNew, void* __p) noexcept {
return __p;
}
namespace kj {
template <typename T, typename... Params>
inline void ctor(T& location, Params&&... params) {
new (internal::PlacementNew(), &location) T(kj::fwd<Params>(params)...);
}
template <typename T>
inline void dtor(T& location) {
location.~T();
}
// =======================================================================================
// Maybe
//
// Use in cases where you want to indicate that a value may be null. Using Maybe<T&> instead of T*
// forces the caller to handle the null case in order to satisfy the compiler, thus reliably
// preventing null pointer dereferences at runtime.
//
// Maybe<T> can be implicitly constructed from T and from nullptr. Additionally, it can be
// implicitly constructed from T*, in which case the pointer is checked for nullness at runtime.
// To read the value of a Maybe<T>, do:
//
// KJ_IF_MAYBE(value, someFuncReturningMaybe()) {
// doSomething(*value);
// } else {
// maybeWasNull();
// }
//
// KJ_IF_MAYBE's first parameter is a variable name which will be defined within the following
// block. The variable will behave like a (guaranteed non-null) pointer to the Maybe's value,
// though it may or may not actually be a pointer.
//
// Note that Maybe<T&> actually just wraps a pointer, whereas Maybe<T> wraps a T and a boolean
// indicating nullness.
template <typename T>
class Maybe;
namespace internal {
template <typename T>
class NullableValue {
// Class whose interface behaves much like T*, but actually contains an instance of T and a
// boolean flag indicating nullness.
public:
inline NullableValue(NullableValue&& other) noexcept(noexcept(T(instance<T&&>())))
: isSet(other.isSet) {
if (isSet) {
ctor(value, kj::mv(other.value));
}
}
inline NullableValue(const NullableValue& other)
: isSet(other.isSet) {
if (isSet) {
ctor(value, other.value);
}
}
inline ~NullableValue() {
if (isSet) {
dtor(value);
}
}
inline T& operator*() { return value; }
inline const T& operator*() const { return value; }
inline T* operator->() { return &value; }
inline const T* operator->() const { return &value; }
inline operator T*() { return isSet ? &value : nullptr; }
inline operator const T*() const { return isSet ? &value : nullptr; }
private: // internal interface used by friends only
inline NullableValue() noexcept: isSet(false) {}
inline NullableValue(T&& t) noexcept(noexcept(T(instance<T&&>())))
: isSet(true) {
ctor(value, kj::mv(t));
}
inline NullableValue(const T& t)
: isSet(true) {
ctor(value, t);
}
inline NullableValue(const T* t)
: isSet(t != nullptr) {
if (isSet) ctor(value, *t);
}
template <typename U>
inline NullableValue(NullableValue<U>&& other) noexcept(noexcept(T(instance<U&&>())))
: isSet(other.isSet) {
if (isSet) {
ctor(value, kj::mv(other.value));
}
}
template <typename U>
inline NullableValue(const NullableValue<U>& other)
: isSet(other.isSet) {
if (isSet) {
ctor(value, other.value);
}
}
template <typename U>
inline NullableValue(const NullableValue<U&>& other)
: isSet(other.isSet) {
if (isSet) {
ctor(value, *other.ptr);
}
}
inline NullableValue(decltype(nullptr)): isSet(false) {}
inline NullableValue& operator=(NullableValue&& other) {
if (&other != this) {
if (isSet) {
dtor(value);
}
isSet = other.isSet;
if (isSet) {
ctor(value, kj::mv(other.value));
}
}
return *this;
}
inline NullableValue& operator=(const NullableValue& other) {
if (&other != this) {
if (isSet) {
dtor(value);
}
isSet = other.isSet;
if (isSet) {
ctor(value, other.value);
}
}
return *this;
}
inline bool operator==(decltype(nullptr)) const { return !isSet; }
inline bool operator!=(decltype(nullptr)) const { return isSet; }
private:
bool isSet;
union {
T value;
};
friend class kj::Maybe<T>;
template <typename U>
friend NullableValue<U>&& readMaybe(Maybe<U>&& maybe);
};
template <typename T>
inline NullableValue<T>&& readMaybe(Maybe<T>&& maybe) { return kj::mv(maybe.ptr); }
template <typename T>
inline T* readMaybe(Maybe<T>& maybe) { return maybe.ptr; }
template <typename T>
inline const T* readMaybe(const Maybe<T>& maybe) { return maybe.ptr; }
template <typename T>
inline T* readMaybe(Maybe<T&>&& maybe) { return maybe.ptr; }
template <typename T>
inline T* readMaybe(const Maybe<T&>& maybe) { return maybe.ptr; }
} // namespace internal
#define KJ_IF_MAYBE(name, exp) if (auto name = ::kj::internal::readMaybe(exp))
template <typename T>
class Maybe {
public:
Maybe(): ptr(nullptr) {}
Maybe(T&& t) noexcept(noexcept(T(instance<T&&>()))): ptr(kj::mv(t)) {}
Maybe(const T& t): ptr(t) {}
Maybe(const T* t) noexcept: ptr(t) {}
Maybe(Maybe&& other) noexcept(noexcept(T(instance<T&&>()))): ptr(kj::mv(other.ptr)) {}
Maybe(const Maybe& other): ptr(other.ptr) {}
template <typename U>
Maybe(Maybe<U>&& other) noexcept(noexcept(T(instance<U&&>()))) {
KJ_IF_MAYBE(val, kj::mv(other)) {
ptr = *val;
}
}
template <typename U>
Maybe(const Maybe<U>& other) {
KJ_IF_MAYBE(val, other) {
ptr = *val;
}
}
Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
inline Maybe& operator=(Maybe&& other) { ptr = kj::mv(other.ptr); return *this; }
inline Maybe& operator=(const Maybe& other) { ptr = other.ptr; return *this; }
inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; }
~Maybe() noexcept {}
private:
internal::NullableValue<T> ptr;
template <typename U>
friend class Maybe;
template <typename U>
friend internal::NullableValue<U>&& internal::readMaybe(Maybe<U>&& maybe);
template <typename U>
friend U* internal::readMaybe(Maybe<U>& maybe);
template <typename U>
friend const U* internal::readMaybe(const Maybe<U>& maybe);
};
template <typename T>
class Maybe<T&> {
public:
Maybe(): ptr(nullptr) {}
Maybe(T& t) noexcept: ptr(&t) {}
Maybe(T* t) noexcept: ptr(t) {}
Maybe(const Maybe& other) noexcept: ptr(other.ptr) {}
template <typename U>
Maybe(const Maybe<U&>& other): ptr(other.ptr) {}
Maybe(decltype(nullptr)) noexcept: ptr(nullptr) {}
inline Maybe& operator=(const Maybe& other) { ptr = other.ptr; return *this; }
inline bool operator==(decltype(nullptr)) const { return ptr == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return ptr != nullptr; }
~Maybe() noexcept {}
private:
T* ptr;
template <typename U>
friend class Maybe;
template <typename U>
friend U* internal::readMaybe(Maybe<U&>&& maybe);
template <typename U>
friend U* internal::readMaybe(const Maybe<U&>& maybe);
};
// ======================================================================================= // =======================================================================================
// Upcast/downcast // Upcast/downcast
...@@ -194,17 +461,23 @@ To upcast(From&& from) { ...@@ -194,17 +461,23 @@ To upcast(From&& from) {
} }
template <typename To, typename From> template <typename To, typename From>
To dynamicDowncastIfAvailable(From* from) { Maybe<To&> dynamicDowncastIfAvailable(From& from) {
// If RTTI is disabled, always returns nullptr. Otherwise, works like dynamic_cast. Useful // If RTTI is disabled, always returns nullptr. Otherwise, works like dynamic_cast. Useful
// in situations where dynamic_cast could allow an optimization, but isn't strictly necessary // in situations where dynamic_cast could allow an optimization, but isn't strictly necessary
// for correctness. It is highly recommended that you try to arrange all your dynamic_casts // for correctness. It is highly recommended that you try to arrange all your dynamic_casts
// this way, as a dynamic_cast that is necessary for correctness implies a flaw in the interface // this way, as a dynamic_cast that is necessary for correctness implies a flaw in the interface
// design. // design.
// Force a compile error if To is not a subtype of From. Cross-casting is rare; if it is needed
// we should have a separate cast function like dynamicCrosscastIfAvailable().
if (false) {
kj::upcast<From*>(upcast<RemoveReference<To>*>(nullptr));
}
#if KJ_NO_RTTI #if KJ_NO_RTTI
return nullptr; return nullptr;
#else #else
return dynamic_cast<To>(from); return dynamic_cast<RemoveReference<To>*>(&from);
#endif #endif
} }
...@@ -229,9 +502,9 @@ To downcast(From* from) { ...@@ -229,9 +502,9 @@ To downcast(From* from) {
} }
template <typename To, typename From> template <typename To, typename From>
To downcast(From&& from) { To downcast(From& from) {
// Reference version of downcast(). // Reference version of downcast().
return *kj::downcast<To*>(&from); return *kj::downcast<RemoveReference<To>*>(&from);
} }
} // namespace kj } // namespace kj
......
...@@ -67,8 +67,8 @@ Exception::Exception(const Exception& other) noexcept ...@@ -67,8 +67,8 @@ Exception::Exception(const Exception& other) noexcept
description(str(other.description)), traceCount(other.traceCount) { description(str(other.description)), traceCount(other.traceCount) {
memcpy(trace, other.trace, sizeof(trace[0]) * traceCount); memcpy(trace, other.trace, sizeof(trace[0]) * traceCount);
if (other.context != nullptr) { KJ_IF_MAYBE(c, other.context) {
context = heap<Context>(**other.context); context = heap<Context>(**c);
} }
} }
...@@ -76,8 +76,8 @@ Exception::~Exception() noexcept {} ...@@ -76,8 +76,8 @@ Exception::~Exception() noexcept {}
Exception::Context::Context(const Context& other) noexcept Exception::Context::Context(const Context& other) noexcept
: file(other.file), line(other.line), description(str(other.description)) { : file(other.file), line(other.line), description(str(other.description)) {
if (other.next != nullptr) { KJ_IF_MAYBE(n, other.next) {
next = heap<Context>(**other.next); next = heap<Context>(**n);
} }
} }
...@@ -89,20 +89,28 @@ const char* Exception::what() const noexcept { ...@@ -89,20 +89,28 @@ const char* Exception::what() const noexcept {
uint contextDepth = 0; uint contextDepth = 0;
const Maybe<Own<Context>>* contextPtr = &context; const Maybe<Own<Context>>* contextPtr = &context;
while (*contextPtr != nullptr) { for (;;) {
KJ_IF_MAYBE(c, *contextPtr) {
++contextDepth; ++contextDepth;
contextPtr = &(***contextPtr).next; contextPtr = &(*c)->next;
} else {
break;
}
} }
Array<Array<char>> contextText = newArray<Array<char>>(contextDepth); Array<Array<char>> contextText = newArray<Array<char>>(contextDepth);
contextDepth = 0; contextDepth = 0;
contextPtr = &context; contextPtr = &context;
while (*contextPtr != nullptr) { for (;;) {
const Context& node = ***contextPtr; KJ_IF_MAYBE(c, *contextPtr) {
const Context& node = **c;
contextText[contextDepth++] = contextText[contextDepth++] =
str(node.file, ":", node.line, ": context: ", node.description, "\n"); str(node.file, ":", node.line, ": context: ", node.description, "\n");
contextPtr = &node.next; contextPtr = &node.next;
} else {
break;
}
} }
// Must be careful to NUL-terminate this. // Must be careful to NUL-terminate this.
......
...@@ -88,10 +88,10 @@ public: ...@@ -88,10 +88,10 @@ public:
}; };
inline Maybe<const Context&> getContext() const { inline Maybe<const Context&> getContext() const {
if (context == nullptr) { KJ_IF_MAYBE(c, context) {
return nullptr; return **c;
} else { } else {
return **context; return nullptr;
} }
} }
......
...@@ -34,214 +34,6 @@ ...@@ -34,214 +34,6 @@
namespace kj { namespace kj {
// #including <new> pulls in a lot of crap, but we want placement news. But operator new cannot
// be defined in a namespace, and defining it globally conflicts with the standard library
// definition. So...
namespace internal {
struct PlacementNew {};
} // namespace internal;
} // namespace kj
inline void* operator new(std::size_t, kj::internal::PlacementNew, void* __p) noexcept {
return __p;
}
namespace kj {
template <typename T, typename... Params>
void constructAt(T* location, Params&&... params) {
new (internal::PlacementNew(), location) T(kj::fwd<Params>(params)...);
}
// =======================================================================================
// Maybe
template <typename T>
class Maybe {
public:
Maybe(): isSet(false) {}
Maybe(T&& t)
: isSet(true) {
constructAt(&value, kj::mv(t));
}
Maybe(const T& t)
: isSet(true) {
constructAt(&value, t);
}
Maybe(Maybe&& other) noexcept(noexcept(T(kj::mv(other.value))))
: isSet(other.isSet) {
if (isSet) {
constructAt(&value, kj::mv(other.value));
}
}
Maybe(const Maybe& other)
: isSet(other.isSet) {
if (isSet) {
constructAt(&value, other.value);
}
}
template <typename U>
Maybe(Maybe<U>&& other) noexcept(noexcept(T(kj::mv(other.value))))
: isSet(other.isSet) {
if (isSet) {
constructAt(&value, kj::mv(other.value));
}
}
template <typename U>
Maybe(const Maybe<U>& other)
: isSet(other.isSet) {
if (isSet) {
constructAt(&value, other.value);
}
}
template <typename U>
Maybe(const Maybe<U&>& other)
: isSet(other.isSet) {
if (isSet) {
constructAt(&value, *other.ptr);
}
}
Maybe(std::nullptr_t): isSet(false) {}
~Maybe() {
if (isSet) {
value.~T();
}
}
template <typename... Params>
inline void init(Params&&... params) {
if (isSet) {
value.~T();
}
isSet = true;
constructAt(&value, kj::fwd(params)...);
}
inline T& operator*() { return value; }
inline const T& operator*() const { return value; }
inline T* operator->() { return &value; }
inline const T* operator->() const { return &value; }
inline Maybe& operator=(Maybe&& other) {
if (&other != this) {
if (isSet) {
value.~T();
}
isSet = other.isSet;
if (isSet) {
constructAt(&value, kj::mv(other.value));
}
}
return *this;
}
inline Maybe& operator=(const Maybe& other) {
if (&other != this) {
if (isSet) {
value.~T();
}
isSet = other.isSet;
if (isSet) {
constructAt(&value, other.value);
}
}
return *this;
}
bool operator==(const Maybe& other) const {
if (isSet == other.isSet) {
if (isSet) {
return value == other.value;
} else {
return true;
}
}
return false;
}
inline bool operator!=(const Maybe& other) const { return !(*this == other); }
inline bool operator==(std::nullptr_t) const { return !isSet; }
inline bool operator!=(std::nullptr_t) const { return isSet; }
template <typename Func>
auto map(const Func& func) const -> Maybe<decltype(func(instance<const T&>()))> {
// Construct a new Maybe by applying the given function to the Maybe's value.
if (isSet) {
return func(value);
} else {
return nullptr;
}
}
template <typename Func>
auto map(const Func& func) -> Maybe<decltype(func(instance<T&>()))> {
// Construct a new Maybe by applying the given function to the Maybe's value.
if (isSet) {
return func(value);
} else {
return nullptr;
}
}
template <typename Func>
auto moveMap(const Func& func) -> Maybe<decltype(func(instance<T&&>()))> {
// Like map() but allows the function to take an rvalue reference to the value.
if (isSet) {
return func(kj::mv(value));
} else {
return nullptr;
}
}
private:
bool isSet;
union {
T value;
};
template <typename U>
friend class Maybe;
};
template <typename T>
class Maybe<T&> {
public:
Maybe(): ptr(nullptr) {}
Maybe(T& t): ptr(&t) {}
Maybe(std::nullptr_t): ptr(nullptr) {}
template <typename U>
Maybe(const Maybe<U>& other) {
if (other == nullptr) {
ptr = nullptr;
} else {
ptr = other.operator->();
}
}
~Maybe() noexcept {}
inline T& operator*() { return *ptr; }
inline const T& operator*() const { return *ptr; }
inline T* operator->() { return ptr; }
inline const T* operator->() const { return ptr; }
inline bool operator==(const Maybe& other) const { return ptr == other.ptr; }
inline bool operator!=(const Maybe& other) const { return ptr != other.ptr; }
inline bool operator==(std::nullptr_t) const { return ptr == nullptr; }
inline bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
private:
T* ptr;
template <typename U>
friend class Maybe;
};
// ======================================================================================= // =======================================================================================
// Own<T> -- An owned pointer. // Own<T> -- An owned pointer.
......
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