Commit 36c3f8e9 authored by Kenton Varda's avatar Kenton Varda

Stringification for all.

parent 47194adc
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define CAPNPROTO_BLOB_H_ #define CAPNPROTO_BLOB_H_
#include <kj/common.h> #include <kj/common.h>
#include <kj/string.h>
#include "common.h" #include "common.h"
#include <string.h> #include <string.h>
...@@ -133,6 +134,11 @@ public: ...@@ -133,6 +134,11 @@ public:
inline operator const char*() const { return data(); } inline operator const char*() const { return data(); }
}; };
inline kj::StringPtr KJ_STRINGIFY(Text::Reader reader) {
// TODO(soon): Use size().
return reader.c_str();
}
class Data::Builder { class Data::Builder {
// Like Data::Reader except the pointers aren't const, and it can't be implicitly constructed from // Like Data::Reader except the pointers aren't const, and it can't be implicitly constructed from
// other types. // other types.
...@@ -187,6 +193,8 @@ public: ...@@ -187,6 +193,8 @@ public:
inline char* begin() const { return bytes; } inline char* begin() const { return bytes; }
inline char* end() const { return bytes + size_; } inline char* end() const { return bytes + size_; }
inline Data::Reader asReader() { return Data::Reader(bytes, size_); }
private: private:
char* bytes; char* bytes;
uint size_; uint size_;
...@@ -207,10 +215,17 @@ public: ...@@ -207,10 +215,17 @@ public:
inline operator char*() const { return data(); } inline operator char*() const { return data(); }
inline operator const char*() const { return data(); } inline operator const char*() const { return data(); }
inline Text::Reader asReader() { return Text::Reader(begin(), size()); }
private: private:
static char nulstr[1]; static char nulstr[1];
}; };
inline kj::StringPtr KJ_STRINGIFY(Text::Builder builder) {
// TODO(soon): Use size().
return builder.c_str();
}
inline bool operator==(const char* a, Data::Reader b) { return Data::Reader(a) == b; } inline bool operator==(const char* a, Data::Reader b) { return Data::Reader(a) == b; }
inline bool operator==(const char* a, Data::Builder b) { return Data::Reader(a) == (Data::Reader)b; } inline bool operator==(const char* a, Data::Builder b) { return Data::Reader(a) == (Data::Reader)b; }
inline bool operator==(Data::Reader a, Data::Builder b) { return a == (Data::Reader)b; } inline bool operator==(Data::Reader a, Data::Builder b) { return a == (Data::Reader)b; }
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#include <kj/io.h> #include <kj/io.h>
#include "../schema-loader.h" #include "../schema-loader.h"
#include "../dynamic.h" #include "../dynamic.h"
#include "../stringify.h"
#include <unistd.h> #include <unistd.h>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
...@@ -67,8 +66,8 @@ private: ...@@ -67,8 +66,8 @@ private:
void fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest); void fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest);
template <typename T> template <typename T>
auto toContainer(T&& t) -> decltype(kj::STR * t) { auto toContainer(T&& t) -> decltype(kj::toCharSequence(kj::fwd(t))) {
return kj::STR * t; return kj::toCharSequence(kj::fwd(t));
} }
TextBlob&& toContainer(TextBlob&& t) { TextBlob&& toContainer(TextBlob&& t) {
return kj::mv(t); return kj::mv(t);
...@@ -135,7 +134,7 @@ void TextBlob::fill(char* textPos, Branch* branchesPos) { ...@@ -135,7 +134,7 @@ void TextBlob::fill(char* textPos, Branch* branchesPos) {
template <typename First, typename... Rest> template <typename First, typename... Rest>
void TextBlob::fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest) { void TextBlob::fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest) {
textPos = kj::fill(textPos, kj::fwd<First>(first)); textPos = kj::internal::fill(textPos, kj::fwd<First>(first));
fill(textPos, branchesPos, kj::fwd<Rest>(rest)...); fill(textPos, branchesPos, kj::fwd<Rest>(rest)...);
} }
...@@ -206,8 +205,6 @@ struct Indent { ...@@ -206,8 +205,6 @@ struct Indent {
inline Iterator end() const { return Iterator(amount); } inline Iterator end() const { return Iterator(amount); }
}; };
inline Indent operator*(kj::Stringifier, Indent i) { return i; }
// ======================================================================================= // =======================================================================================
SchemaLoader schemaLoader; SchemaLoader schemaLoader;
...@@ -365,13 +362,13 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema ...@@ -365,13 +362,13 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema
case schema::Value::Body::UINT64_VALUE: return text(body.getUint64Value()); case schema::Value::Body::UINT64_VALUE: return text(body.getUint64Value());
case schema::Value::Body::FLOAT32_VALUE: return text(body.getFloat32Value()); case schema::Value::Body::FLOAT32_VALUE: return text(body.getFloat32Value());
case schema::Value::Body::FLOAT64_VALUE: return text(body.getFloat64Value()); case schema::Value::Body::FLOAT64_VALUE: return text(body.getFloat64Value());
case schema::Value::Body::TEXT_VALUE: return stringify(body.getTextValue()); case schema::Value::Body::TEXT_VALUE: return text(DynamicValue::Reader(body.getTextValue()));
case schema::Value::Body::DATA_VALUE: return stringify(body.getDataValue()); case schema::Value::Body::DATA_VALUE: return text(DynamicValue::Reader(body.getDataValue()));
case schema::Value::Body::LIST_VALUE: { case schema::Value::Body::LIST_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::LIST_TYPE, "type/value mismatch"); PRECOND(type.getBody().which() == schema::Type::Body::LIST_TYPE, "type/value mismatch");
auto value = body.getListValue<DynamicList>( auto value = body.getListValue<DynamicList>(
ListSchema::of(type.getBody().getListType(), scope)); ListSchema::of(type.getBody().getListType(), scope));
return text(stringify(value)); return text(value);
} }
case schema::Value::Body::ENUM_VALUE: { case schema::Value::Body::ENUM_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::ENUM_TYPE, "type/value mismatch"); PRECOND(type.getBody().which() == schema::Type::Body::ENUM_TYPE, "type/value mismatch");
...@@ -386,7 +383,7 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema ...@@ -386,7 +383,7 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema
PRECOND(type.getBody().which() == schema::Type::Body::STRUCT_TYPE, "type/value mismatch"); PRECOND(type.getBody().which() == schema::Type::Body::STRUCT_TYPE, "type/value mismatch");
auto value = body.getStructValue<DynamicStruct>( auto value = body.getStructValue<DynamicStruct>(
scope.getDependency(type.getBody().getStructType()).asStruct()); scope.getDependency(type.getBody().getStructType()).asStruct());
return text(stringify(value)); return text(value);
} }
case schema::Value::Body::INTERFACE_VALUE: { case schema::Value::Body::INTERFACE_VALUE: {
return text(""); return text("");
......
...@@ -1318,6 +1318,27 @@ DynamicList::Reader DynamicList::Builder::asReader() { ...@@ -1318,6 +1318,27 @@ DynamicList::Reader DynamicList::Builder::asReader() {
// ======================================================================================= // =======================================================================================
DynamicValue::Reader DynamicValue::Builder::asReader() {
switch (type) {
case UNKNOWN: return Reader();
case VOID: return Reader(voidValue);
case BOOL: return Reader(boolValue);
case INT: return Reader(intValue);
case UINT: return Reader(uintValue);
case FLOAT: return Reader(floatValue);
case TEXT: return Reader(textValue.asReader());
case DATA: return Reader(dataValue.asReader());
case LIST: return Reader(listValue.asReader());
case ENUM: return Reader(enumValue);
case STRUCT: return Reader(structValue.asReader());
case UNION: return Reader(unionValue.asReader());
case INTERFACE: FAIL_CHECK("Interfaces not implemented."); return Reader();
case OBJECT: return Reader(objectValue);
}
FAIL_CHECK("Missing switch case.");
return Reader();
}
namespace { namespace {
template <typename T> template <typename T>
......
...@@ -191,6 +191,9 @@ private: ...@@ -191,6 +191,9 @@ private:
: schema(schema), reader(reader) {} : schema(schema), reader(reader) {}
friend struct DynamicStruct; friend struct DynamicStruct;
friend class DynamicUnion::Builder;
friend kj::String internal::unionString(
internal::StructReader reader, const internal::RawSchema& schema, uint memberIndex);
}; };
class DynamicUnion::Builder { class DynamicUnion::Builder {
...@@ -227,6 +230,8 @@ public: ...@@ -227,6 +230,8 @@ public:
Data::Builder initObjectAsData(Text::Reader name, uint size); Data::Builder initObjectAsData(Text::Reader name, uint size);
// Convenience methods that identify the member by text name. // Convenience methods that identify the member by text name.
Reader asReader();
private: private:
StructSchema::Union schema; StructSchema::Union schema;
internal::StructBuilder builder; internal::StructBuilder builder;
...@@ -287,7 +292,7 @@ private: ...@@ -287,7 +292,7 @@ private:
friend class MessageBuilder; friend class MessageBuilder;
template <typename T, ::capnproto::Kind k> template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::ToDynamic_; friend struct ::capnproto::ToDynamic_;
friend kj::String internal::debugString( friend kj::String internal::structString(
internal::StructReader reader, const internal::RawSchema& schema); internal::StructReader reader, const internal::RawSchema& schema);
}; };
...@@ -644,6 +649,17 @@ private: ...@@ -644,6 +649,17 @@ private:
// specialization. Has a method apply() which does the work. // specialization. Has a method apply() which does the work.
}; };
kj::String KJ_STRINGIFY(DynamicValue::Reader value);
kj::String KJ_STRINGIFY(DynamicValue::Builder value);
kj::String KJ_STRINGIFY(DynamicEnum value);
kj::String KJ_STRINGIFY(DynamicObject value);
kj::String KJ_STRINGIFY(DynamicUnion::Reader value);
kj::String KJ_STRINGIFY(DynamicUnion::Builder value);
kj::String KJ_STRINGIFY(DynamicStruct::Reader value);
kj::String KJ_STRINGIFY(DynamicStruct::Builder value);
kj::String KJ_STRINGIFY(DynamicList::Reader value);
kj::String KJ_STRINGIFY(DynamicList::Builder value);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Inject the ability to use DynamicStruct for message roots and Dynamic{Struct,List} for // Inject the ability to use DynamicStruct for message roots and Dynamic{Struct,List} for
// generated Object accessors. // generated Object accessors.
...@@ -869,6 +885,12 @@ struct DynamicObject::AsImpl<T, Kind::LIST> { ...@@ -869,6 +885,12 @@ struct DynamicObject::AsImpl<T, Kind::LIST> {
// ------------------------------------------------------------------- // -------------------------------------------------------------------
inline DynamicUnion::Reader DynamicUnion::Builder::asReader() {
return DynamicUnion::Reader(schema, builder.asReader());
}
// -------------------------------------------------------------------
template <typename T> template <typename T>
typename T::Reader DynamicStruct::Reader::as() { typename T::Reader DynamicStruct::Reader::as() {
static_assert(kind<T>() == Kind::STRUCT, static_assert(kind<T>() == Kind::STRUCT,
......
...@@ -172,13 +172,29 @@ inline const RawSchema& rawSchema() { ...@@ -172,13 +172,29 @@ inline const RawSchema& rawSchema() {
template <typename T> template <typename T>
struct TypeIdFor; struct TypeIdFor;
kj::String debugString(StructReader reader, const RawSchema& schema); template <typename T>
// Declared here so that we can declare inline debugString() methods on generated types. struct UnionMemberIndexFor;
template <typename T>
inline uint unionMemberIndex() { return UnionMemberIndexFor<T>::value; }
template <typename T>
struct UnionParentTypeFor;
template <typename T>
using UnionParentType = typename UnionParentTypeFor<T>::Type;
kj::String structString(StructReader reader, const RawSchema& schema);
kj::String unionString(StructReader reader, const RawSchema& schema, uint memberIndex);
// Declared here so that we can declare inline stringify methods on generated types.
// Defined in stringify.c++, which depends on dynamic.c++, which is allowed not to be linked in. // Defined in stringify.c++, which depends on dynamic.c++, which is allowed not to be linked in.
template <typename T> template <typename T>
inline kj::String debugString(StructReader reader) { inline kj::String structString(StructReader reader) {
return debugString(reader, rawSchema<T>()); return structString(reader, rawSchema<T>());
}
template <typename T>
inline kj::String unionString(StructReader reader) {
return unionString(reader, rawSchema<UnionParentType<T>>(), unionMemberIndex<T>());
} }
} // namespace internal } // namespace internal
...@@ -199,6 +215,7 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; } ...@@ -199,6 +215,7 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; }
#define CAPNPROTO_DEFINE_ENUM(type) \ #define CAPNPROTO_DEFINE_ENUM(type) \
constexpr Kind KindOf<type>::kind; \ constexpr Kind KindOf<type>::kind; \
constexpr uint64_t TypeIdFor<type>::typeId; constexpr uint64_t TypeIdFor<type>::typeId;
#define CAPNPROTO_DECLARE_STRUCT(type, id, dataWordSize, pointerCount, preferredElementEncoding) \ #define CAPNPROTO_DECLARE_STRUCT(type, id, dataWordSize, pointerCount, preferredElementEncoding) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::STRUCT; }; \ template <> struct KindOf<type> { static constexpr Kind kind = Kind::STRUCT; }; \
template <> struct StructSizeFor<type> { \ template <> struct StructSizeFor<type> { \
...@@ -213,6 +230,15 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; } ...@@ -213,6 +230,15 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; }
constexpr Kind KindOf<type>::kind; \ constexpr Kind KindOf<type>::kind; \
constexpr StructSize StructSizeFor<type>::value; \ constexpr StructSize StructSizeFor<type>::value; \
constexpr uint64_t TypeIdFor<type>::typeId; constexpr uint64_t TypeIdFor<type>::typeId;
#define CAPNPROTO_DECLARE_UNION(type, parentType, memberIndex) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::UNION; }; \
template <> struct UnionMemberIndexFor<type> { static constexpr uint value = memberIndex; }; \
template <> struct UnionParentTypeFor<type> { typedef parentType Type; }
#define CAPNPROTO_DEFINE_UNION(type) \
constexpr Kind KindOf<type>::kind; \
constexpr uint UnionMemberIndexFor<type>::value;
#define CAPNPROTO_DECLARE_INTERFACE(type, id) \ #define CAPNPROTO_DECLARE_INTERFACE(type, id) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::INTERFACE; }; \ template <> struct KindOf<type> { static constexpr Kind kind = Kind::INTERFACE; }; \
template <> struct TypeIdFor<type> { static constexpr uint64_t typeId = 0x##id; }; \ template <> struct TypeIdFor<type> { static constexpr uint64_t typeId = 0x##id; }; \
......
...@@ -126,6 +126,7 @@ enum class Kind: uint8_t { ...@@ -126,6 +126,7 @@ enum class Kind: uint8_t {
BLOB, BLOB,
ENUM, ENUM,
STRUCT, STRUCT,
UNION,
INTERFACE, INTERFACE,
LIST, LIST,
UNKNOWN UNKNOWN
......
...@@ -42,8 +42,7 @@ TEST(SchemaLoader, Load) { ...@@ -42,8 +42,7 @@ TEST(SchemaLoader, Load) {
Schema struct8Schema = loader.load(Schema::from<test::TestLists::Struct8>().getProto()); Schema struct8Schema = loader.load(Schema::from<test::TestLists::Struct8>().getProto());
Schema structPSchema = loader.load(Schema::from<test::TestLists::StructP>().getProto()); Schema structPSchema = loader.load(Schema::from<test::TestLists::StructP>().getProto());
EXPECT_STREQ(nativeSchema.getProto().debugString().cStr(), EXPECT_EQ(kj::str(nativeSchema.getProto()), kj::str(testListsSchema.getProto()));
testListsSchema.getProto().debugString().cStr());
EXPECT_FALSE(testListsSchema == nativeSchema); EXPECT_FALSE(testListsSchema == nativeSchema);
EXPECT_FALSE(struct32Schema == Schema::from<test::TestLists::Struct32>()); EXPECT_FALSE(struct32Schema == Schema::from<test::TestLists::Struct32>());
...@@ -165,8 +164,8 @@ TEST(SchemaLoader, Upgrade) { ...@@ -165,8 +164,8 @@ TEST(SchemaLoader, Upgrade) {
StructSchema schema = loader.get(typeId<test::TestOldVersion>()).asStruct(); StructSchema schema = loader.get(typeId<test::TestOldVersion>()).asStruct();
EXPECT_STREQ(Schema::from<test::TestOldVersion>().getProto().debugString().cStr(), EXPECT_EQ(kj::str(Schema::from<test::TestOldVersion>().getProto()),
schema.getProto().debugString().cStr()); kj::str(schema.getProto()));
loadUnderAlternateTypeId<test::TestNewVersion>(loader, typeId<test::TestOldVersion>()); loadUnderAlternateTypeId<test::TestNewVersion>(loader, typeId<test::TestOldVersion>());
...@@ -185,8 +184,7 @@ TEST(SchemaLoader, Downgrade) { ...@@ -185,8 +184,7 @@ TEST(SchemaLoader, Downgrade) {
StructSchema schema = loader.get(typeId<test::TestNewVersion>()).asStruct(); StructSchema schema = loader.get(typeId<test::TestNewVersion>()).asStruct();
EXPECT_STREQ(Schema::from<test::TestNewVersion>().getProto().debugString().cStr(), EXPECT_EQ(kj::str(Schema::from<test::TestNewVersion>().getProto()), kj::str(schema.getProto()));
schema.getProto().debugString().cStr());
loadUnderAlternateTypeId<test::TestOldVersion>(loader, typeId<test::TestNewVersion>()); loadUnderAlternateTypeId<test::TestOldVersion>(loader, typeId<test::TestNewVersion>());
......
...@@ -124,8 +124,10 @@ private: ...@@ -124,8 +124,10 @@ private:
return StructSchema(&internal::rawSchema<T>()); return StructSchema(&internal::rawSchema<T>());
} }
friend class Schema; friend class Schema;
friend kj::String internal::debugString( friend kj::String internal::structString(
internal::StructReader reader, const internal::RawSchema& schema); internal::StructReader reader, const internal::RawSchema& schema);
friend kj::String internal::unionString(
internal::StructReader reader, const internal::RawSchema& schema, uint memberIndex);
}; };
class StructSchema::Member { class StructSchema::Member {
......
...@@ -45,12 +45,18 @@ ...@@ -45,12 +45,18 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE #define CAPNPROTO_PRIVATE
#include "stringify.h"
#include "message.h" #include "message.h"
#include "dynamic.h"
#include <kj/logging.h> #include <kj/logging.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "test-util.h" #include "test-util.h"
namespace kj {
inline std::ostream& operator<<(std::ostream& os, const kj::String& s) {
return os.write(s.begin(), s.size());
}
}
namespace capnproto { namespace capnproto {
namespace internal { namespace internal {
namespace { namespace {
...@@ -59,11 +65,11 @@ TEST(Stringify, DebugString) { ...@@ -59,11 +65,11 @@ TEST(Stringify, DebugString) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>(); auto root = builder.initRoot<TestAllTypes>();
EXPECT_STREQ("()", root.debugString().cStr()); EXPECT_EQ("()", kj::str(root));
initTestMessage(root); initTestMessage(root);
EXPECT_STREQ("(" EXPECT_EQ("("
"boolField = true, " "boolField = true, "
"int8Field = -123, " "int8Field = -123, "
"int16Field = -12345, " "int16Field = -12345, "
...@@ -134,17 +140,39 @@ TEST(Stringify, DebugString) { ...@@ -134,17 +140,39 @@ TEST(Stringify, DebugString) {
"(textField = \"structlist 2\"), " "(textField = \"structlist 2\"), "
"(textField = \"structlist 3\")], " "(textField = \"structlist 3\")], "
"enumList = [foo, garply])", "enumList = [foo, garply])",
root.debugString().cStr()); kj::str(root));
}
TEST(Stringify, Unions) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestUnion>();
root.getUnion0().setU0f0s16(321);
root.getUnion1().setU1f0sp("foo");
root.getUnion2().setU2f0s1(true);
root.getUnion3().setU3f0s64(123456789012345678ll);
EXPECT_EQ("("
"union0 = u0f0s16(321), "
"union1 = u1f0sp(\"foo\"), "
"union2 = u2f0s1(true), "
"union3 = u3f0s64(123456789012345678))",
kj::str(root));
EXPECT_EQ("u0f0s16(321)", kj::str(root.getUnion0()));
EXPECT_EQ("u1f0sp(\"foo\")", kj::str(root.getUnion1()));
EXPECT_EQ("u2f0s1(true)", kj::str(root.getUnion2()));
EXPECT_EQ("u3f0s64(123456789012345678)", kj::str(root.getUnion3()));
} }
TEST(Stringify, MoreValues) { TEST(Stringify, MoreValues) {
EXPECT_STREQ("123", stringify(123).cStr()); EXPECT_EQ("123", kj::str(DynamicValue::Reader(123)));
EXPECT_STREQ("1.23e47", stringify(123e45).cStr()); EXPECT_EQ("1.23e47", kj::str(DynamicValue::Reader(123e45)));
EXPECT_STREQ("\"foo\"", stringify("foo").cStr()); EXPECT_EQ("\"foo\"", kj::str(DynamicValue::Reader("foo")));
EXPECT_STREQ("\"\\a\\b\\n\\t\\\"\"", stringify("\a\b\n\t\"").cStr()); EXPECT_EQ("\"\\a\\b\\n\\t\\\"\"", kj::str(DynamicValue::Reader("\a\b\n\t\"")));
EXPECT_STREQ("foo", stringify(TestEnum::FOO).cStr()); EXPECT_EQ("foo", kj::str(DynamicValue::Reader(TestEnum::FOO)));
EXPECT_STREQ("123", stringify(static_cast<TestEnum>(123)).cStr()); EXPECT_EQ("123", kj::str(DynamicValue::Reader(static_cast<TestEnum>(123))));
} }
} // namespace } // namespace
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE #define CAPNPROTO_PRIVATE
#include "stringify.h" #include "dynamic.h"
#include <kj/logging.h> #include <kj/logging.h>
#include <sstream> #include <sstream>
...@@ -58,10 +58,10 @@ static void print(std::ostream& os, DynamicValue::Reader value, ...@@ -58,10 +58,10 @@ static void print(std::ostream& os, DynamicValue::Reader value,
break; break;
case DynamicValue::FLOAT: { case DynamicValue::FLOAT: {
if (which == schema::Type::Body::FLOAT32_TYPE) { if (which == schema::Type::Body::FLOAT32_TYPE) {
auto buf = kj::STR * value.as<float>(); auto buf = kj::toCharSequence(value.as<float>());
os.write(buf.begin(), buf.size()); os.write(buf.begin(), buf.size());
} else { } else {
auto buf = kj::STR * value.as<double>(); auto buf = kj::toCharSequence(value.as<double>());
os.write(buf.begin(), buf.size()); os.write(buf.begin(), buf.size());
} }
break; break;
...@@ -170,8 +170,6 @@ static void print(std::ostream& os, DynamicValue::Reader value, ...@@ -170,8 +170,6 @@ static void print(std::ostream& os, DynamicValue::Reader value,
} }
} }
} // namespace
kj::String stringify(DynamicValue::Reader value) { kj::String stringify(DynamicValue::Reader value) {
std::stringstream out; std::stringstream out;
print(out, value, schema::Type::Body::STRUCT_TYPE); print(out, value, schema::Type::Body::STRUCT_TYPE);
...@@ -179,12 +177,30 @@ kj::String stringify(DynamicValue::Reader value) { ...@@ -179,12 +177,30 @@ kj::String stringify(DynamicValue::Reader value) {
return kj::heapString(content.data(), content.size()); return kj::heapString(content.data(), content.size());
} }
} // namespace
kj::String KJ_STRINGIFY(DynamicValue::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicValue::Builder value) { return stringify(value.asReader()); }
kj::String KJ_STRINGIFY(DynamicEnum value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicObject value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicUnion::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicUnion::Builder value) { return stringify(value.asReader()); }
kj::String KJ_STRINGIFY(DynamicStruct::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicStruct::Builder value) { return stringify(value.asReader()); }
kj::String KJ_STRINGIFY(DynamicList::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicList::Builder value) { return stringify(value.asReader()); }
namespace internal { namespace internal {
kj::String debugString(StructReader reader, const RawSchema& schema) { kj::String structString(StructReader reader, const RawSchema& schema) {
return stringify(DynamicStruct::Reader(StructSchema(&schema), reader)); return stringify(DynamicStruct::Reader(StructSchema(&schema), reader));
} }
kj::String unionString(StructReader reader, const RawSchema& schema, uint memberIndex) {
return stringify(DynamicUnion::Reader(
StructSchema(&schema).getMembers()[memberIndex].asUnion(), reader));
}
} // namespace internal } // namespace internal
} // namespace capnproto } // 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.
#ifndef CAPNPROTO_STRINGIFY_H_
#define CAPNPROTO_STRINGIFY_H_
#include "dynamic.h"
namespace capnproto {
kj::String stringify(DynamicValue::Reader value);
// Stringify an arbitrary Cap'n Proto value. Note that DynamicValue::Reader can be implicitly
// constructed from any Cap'n Proto field type, so this will accept pretty much anything.
} // namespace capnproto
#endif // CAPNPROTO_STRINGIFY_H_
...@@ -353,6 +353,12 @@ struct TestLateUnion { ...@@ -353,6 +353,12 @@ struct TestLateUnion {
corge @5 :List(Int32); corge @5 :List(Int32);
grault @6 :Float32; grault @6 :Float32;
} }
anotherUnion @7 union {
qux @8 :Text;
corge @9 :List(Int32);
grault @10 :Float32;
}
} }
struct TestOldVersion { struct TestOldVersion {
......
...@@ -189,6 +189,14 @@ template <typename T> struct Decay_<const T> { typedef typename Decay_<T>::Type ...@@ -189,6 +189,14 @@ template <typename T> struct Decay_<const T> { typedef typename Decay_<T>::Type
template <typename T> struct Decay_<volatile T> { typedef typename Decay_<T>::Type Type; }; template <typename T> struct Decay_<volatile T> { typedef typename Decay_<T>::Type Type; };
template <typename T> using Decay = typename Decay_<T>::Type; template <typename T> using Decay = typename Decay_<T>::Type;
template <bool b> struct EnableIf_;
template <> struct EnableIf_<true> { typedef void Type; };
template <bool b> using EnableIf = typename EnableIf_<b>::Type;
// Use like:
//
// template <typename T, typename = EnableIf<isValid<T>()>
// void func(T&& t);
template <typename T> template <typename T>
T instance() noexcept; T instance() noexcept;
// Like std::declval, but doesn't transform T into an rvalue reference. If you want that, specify // Like std::declval, but doesn't transform T into an rvalue reference. If you want that, specify
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "exception.h" #include "exception.h"
#include "util.h" #include "string.h"
#include "logging.h" #include "logging.h"
#include <unistd.h> #include <unistd.h>
#include <execinfo.h> #include <execinfo.h>
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
namespace kj { namespace kj {
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) { ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature) {
static const char* NATURE_STRINGS[] = { static const char* NATURE_STRINGS[] = {
"precondition not met", "precondition not met",
"bug in code", "bug in code",
...@@ -44,7 +44,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) { ...@@ -44,7 +44,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
return arrayPtr(s, strlen(s)); return arrayPtr(s, strlen(s));
} }
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability) { ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability) {
static const char* DURABILITY_STRINGS[] = { static const char* DURABILITY_STRINGS[] = {
"temporary", "temporary",
"permanent" "permanent"
...@@ -149,7 +149,7 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) { ...@@ -149,7 +149,7 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) {
if (std::uncaught_exception()) { if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n')); logMessage(str("unwind: ", exception.what(), '\n'));
} else { } else {
throw std::move(exception); throw kj::mv(exception);
} }
#endif #endif
} }
...@@ -158,7 +158,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) { ...@@ -158,7 +158,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS #if KJ_NO_EXCEPTIONS
logMessage(str(exception.what(), '\n')); logMessage(str(exception.what(), '\n'));
#else #else
throw std::move(exception); throw kj::mv(exception);
#endif #endif
} }
......
...@@ -116,9 +116,8 @@ private: ...@@ -116,9 +116,8 @@ private:
mutable String whatBuffer; mutable String whatBuffer;
}; };
struct Stringifier; ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature);
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature); ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability);
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability);
class ExceptionCallback { class ExceptionCallback {
// If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order // If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
......
...@@ -31,7 +31,7 @@ namespace kj { ...@@ -31,7 +31,7 @@ namespace kj {
Log::Severity Log::minSeverity = Log::Severity::INFO; Log::Severity Log::minSeverity = Log::Severity::INFO;
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) { ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity) {
static const char* SEVERITY_STRINGS[] = { static const char* SEVERITY_STRINGS[] = {
"info", "info",
"warning", "warning",
...@@ -163,20 +163,20 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro ...@@ -163,20 +163,20 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro
case LOG: case LOG:
break; break;
case ASSERTION: case ASSERTION:
pos = fill(pos, expected, codeArray); pos = internal::fill(pos, expected, codeArray);
break; break;
case SYSCALL: case SYSCALL:
pos = fill(pos, codeArray, colon, sysErrorArray); pos = internal::fill(pos, codeArray, colon, sysErrorArray);
break; break;
} }
for (size_t i = 0; i < argValues.size(); i++) { for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || style != LOG) { if (i > 0 || style != LOG) {
pos = fill(pos, delim); pos = internal::fill(pos, delim);
} }
if (argNames[i].size() > 0 && argNames[i][0] != '\"') { if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
pos = fill(pos, argNames[i], sep); pos = internal::fill(pos, argNames[i], sep);
} }
pos = fill(pos, argValues[i]); pos = internal::fill(pos, argValues[i]);
} }
return result; return result;
......
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
#ifndef KJ_LOGGING_H_ #ifndef KJ_LOGGING_H_
#define KJ_LOGGING_H_ #define KJ_LOGGING_H_
#include "util.h" #include "string.h"
#include "exception.h" #include "exception.h"
namespace kj { namespace kj {
...@@ -208,7 +208,7 @@ private: ...@@ -208,7 +208,7 @@ private:
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR. // Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
}; };
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity); ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity);
#define LOG(severity, ...) \ #define LOG(severity, ...) \
if (!::kj::Log::shouldLog(::kj::Log::Severity::severity)) {} else \ if (!::kj::Log::shouldLog(::kj::Log::Severity::severity)) {} else \
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "util.h" #include "string.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
...@@ -29,7 +29,7 @@ namespace kj { ...@@ -29,7 +29,7 @@ namespace kj {
namespace internal { namespace internal {
namespace { namespace {
TEST(Util, Foo) { TEST(String, Str) {
EXPECT_EQ("foobar", str("foo", "bar")); EXPECT_EQ("foobar", str("foo", "bar"));
EXPECT_EQ("1 2 3 4", str(1, " ", 2u, " ", 3l, " ", 4ll)); EXPECT_EQ("1 2 3 4", str(1, " ", 2u, " ", 3l, " ", 4ll));
EXPECT_EQ("1.5 foo 1e15 bar -3", str(1.5f, " foo ", 1e15, " bar ", -3)); EXPECT_EQ("1.5 foo 1e15 bar -3", str(1.5f, " foo ", 1e15, " bar ", -3));
......
This diff is collapsed.
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#ifndef KJ_STRING_H_ #ifndef KJ_STRING_H_
#define KJ_STRING_H_ #define KJ_STRING_H_
#include <initializer_list>
#include "array.h" #include "array.h"
#include <string.h> #include <string.h>
...@@ -140,6 +141,177 @@ String heapString(StringPtr value); ...@@ -140,6 +141,177 @@ String heapString(StringPtr value);
String heapString(ArrayPtr<const char> value); String heapString(ArrayPtr<const char> value);
// Allocates a copy of the given value on the heap. // Allocates a copy of the given value on the heap.
// =======================================================================================
// Magic str() function which transforms parameters to text and concatenates them into one big
// String.
namespace internal {
inline size_t sum(std::initializer_list<size_t> nums) {
size_t result = 0;
for (auto num: nums) {
result += num;
}
return result;
}
inline char* fill(char* ptr) { return ptr; }
template <typename First, typename... Rest>
char* fill(char* __restrict__ target, const First& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
*target++ = *i++;
}
return fill(target, kj::fwd<Rest>(rest)...);
}
template <typename... Params>
String concat(Params&&... params) {
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `char`.
String result = heapString(sum({params.size()...}));
fill(result.begin(), kj::fwd<Params>(params)...);
return result;
}
inline String concat(String&& arr) {
return kj::mv(arr);
}
struct Stringifier {
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<const char>& s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<char>& s) const { return s; }
template<size_t n>
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline ArrayPtr<const char> operator*(const String& s) const { return s.asArray(); }
inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); }
inline FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
result[0] = c;
return result;
}
StringPtr operator*(bool b) const;
CappedArray<char, sizeof(short) * 4> operator*(short i) const;
CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const;
CappedArray<char, sizeof(int) * 4> operator*(int i) const;
CappedArray<char, sizeof(unsigned int) * 4> operator*(unsigned int i) const;
CappedArray<char, sizeof(long) * 4> operator*(long i) const;
CappedArray<char, sizeof(unsigned long) * 4> operator*(unsigned long i) const;
CappedArray<char, sizeof(long long) * 4> operator*(long long i) const;
CappedArray<char, sizeof(unsigned long long) * 4> operator*(unsigned long long i) const;
CappedArray<char, 24> operator*(float f) const;
CappedArray<char, 32> operator*(double f) const;
CappedArray<char, sizeof(const void*) * 4> operator*(const void* s) const;
template <typename T>
Array<char> operator*(ArrayPtr<T> arr) const;
template <typename T>
Array<char> operator*(const Array<T>& arr) const;
};
static constexpr Stringifier STR = Stringifier();
} // namespace internal
template <typename T>
auto toCharSequence(T&& value) -> decltype(internal::STR * kj::fwd<T>(value)) {
// Returns an iterable of chars that represent a textual representation of the value, suitable
// for debugging.
//
// Most users should use str() instead, but toCharSequence() may occasionally be useful to avoid
// heap allocation overhead that str() implies.
//
// To specialize this function for your type, see KJ_STRINGIFY.
return internal::STR * kj::fwd<T>(value);
}
CappedArray<char, sizeof(unsigned short) * 4> hex(unsigned short i);
CappedArray<char, sizeof(unsigned int) * 4> hex(unsigned int i);
CappedArray<char, sizeof(unsigned long) * 4> hex(unsigned long i);
CappedArray<char, sizeof(unsigned long long) * 4> hex(unsigned long long i);
template <typename... Params>
String str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return internal::concat(toCharSequence(kj::fwd<Params>(params))...);
}
inline String str(String&& s) { return mv(s); }
// Overload to prevent redundant allocation.
template <typename T>
String strArray(T&& arr, const char* delim) {
size_t delimLen = strlen(delim);
KJ_STACK_ARRAY(decltype(internal::STR * arr[0]), pieces, arr.size(), 8, 32);
size_t size = 0;
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) size += delimLen;
pieces[i] = internal::STR * arr[i];
size += pieces[i].size();
}
String result = heapString(size);
char* pos = result.begin();
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) {
memcpy(pos, delim, delimLen);
pos += delimLen;
}
pos = internal::fill(pos, pieces[i]);
}
return result;
}
namespace internal {
template <typename T>
inline Array<char> Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", ");
}
template <typename T>
inline Array<char> Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", ");
}
} // namespace internal
#define KJ_STRINGIFY(...) operator*(::kj::internal::Stringifier, __VA_ARGS__)
// Defines a stringifier for a custom type. Example:
//
// class Foo {...};
// inline StringPtr KJ_STRINGIFY(const Foo& foo) { return foo.name(); }
//
// This allows Foo to be passed to str().
//
// The function should be declared either in the same namespace as the target type or in the global
// namespace. It can return any type which is an iterable container of chars.
// ======================================================================================= // =======================================================================================
// Inline implementation details. // Inline implementation details.
......
This diff is collapsed.
// 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.
#ifndef KJ_UTIL_H_
#define KJ_UTIL_H_
#include <initializer_list>
#include <utility>
#include <type_traits>
#include "array.h"
#include "string.h"
#include <string.h>
namespace kj {
// =======================================================================================
// String stuff
inline size_t sum(std::initializer_list<size_t> nums) {
size_t result = 0;
for (auto num: nums) {
result += num;
}
return result;
}
inline char* fill(char* ptr) { return ptr; }
template <typename First, typename... Rest>
char* fill(char* __restrict__ target, const First& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
*target++ = *i++;
}
return fill(target, std::forward<Rest>(rest)...);
}
template <typename... Params>
String concat(Params&&... params) {
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `Element`.
String result = heapString(sum({params.size()...}));
fill(result.begin(), std::forward<Params>(params)...);
return result;
}
inline String concat(String&& arr) {
return std::move(arr);
}
struct Stringifier {
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<const char>& s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<char>& s) const { return s; }
template<size_t n>
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline ArrayPtr<const char> operator*(const String& s) const { return s.asArray(); }
inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); }
inline FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
result[0] = c;
return result;
}
CappedArray<char, sizeof(short) * 4> operator*(short i) const;
CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const;
CappedArray<char, sizeof(int) * 4> operator*(int i) const;
CappedArray<char, sizeof(unsigned int) * 4> operator*(unsigned int i) const;
CappedArray<char, sizeof(long) * 4> operator*(long i) const;
CappedArray<char, sizeof(unsigned long) * 4> operator*(unsigned long i) const;
CappedArray<char, sizeof(long long) * 4> operator*(long long i) const;
CappedArray<char, sizeof(unsigned long long) * 4> operator*(unsigned long long i) const;
CappedArray<char, 24> operator*(float f) const;
CappedArray<char, 32> operator*(double f) const;
CappedArray<char, sizeof(const void*) * 4> operator*(const void* s) const;
template <typename T>
Array<char> operator*(ArrayPtr<T> arr) const;
template <typename T>
Array<char> operator*(const Array<T>& arr) const;
};
static constexpr Stringifier STR = Stringifier();
CappedArray<char, sizeof(unsigned short) * 4> hex(unsigned short i);
CappedArray<char, sizeof(unsigned int) * 4> hex(unsigned int i);
CappedArray<char, sizeof(unsigned long) * 4> hex(unsigned long i);
CappedArray<char, sizeof(unsigned long long) * 4> hex(unsigned long long i);
template <typename... Params>
String str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return concat(STR * std::forward<Params>(params)...);
}
template <typename T>
String strArray(T&& arr, const char* delim) {
size_t delimLen = strlen(delim);
KJ_STACK_ARRAY(decltype(STR * arr[0]), pieces, arr.size(), 8, 32);
size_t size = 0;
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) size += delimLen;
pieces[i] = STR * arr[i];
size += pieces[i].size();
}
String result = heapString(size);
char* pos = result.begin();
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) {
memcpy(pos, delim, delimLen);
pos += delimLen;
}
pos = fill(pos, pieces[i]);
}
return result;
}
template <typename T>
inline Array<char> Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", ");
}
template <typename T>
inline Array<char> Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", ");
}
template <typename T, typename Func>
auto mapArray(T&& arr, Func&& func) -> Array<decltype(func(arr[0]))> {
// TODO(cleanup): Use ArrayBuilder.
Array<decltype(func(arr[0]))> result = heapArray<decltype(func(arr[0]))>(arr.size());
size_t pos = 0;
for (auto& element: arr) {
result[pos++] = func(element);
}
return result;
}
} // namespace kj
#endif // KJ_UTIL_H_
...@@ -855,6 +855,9 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d ...@@ -855,6 +855,9 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d
unions = [d | DescUnion d <- members] unions = [d | DescUnion d <- members]
(finalDataSize, finalPointerCount) <- (finalDataSize, finalPointerCount) <-
recover (dataSize, pointerCount) $ enforceFixed isFixed (dataSize, pointerCount) recover (dataSize, pointerCount) $ enforceFixed isFixed (dataSize, pointerCount)
let memberByNumber d@(DescField f) = Just (fieldNumber f, d)
memberByNumber d@(DescUnion u) = Just (unionNumber u, d)
memberByNumber _ = Nothing
return (let return (let
in DescStruct StructDesc in DescStruct StructDesc
{ structName = name { structName = name
...@@ -867,6 +870,7 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d ...@@ -867,6 +870,7 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d
, structUnions = unions , structUnions = unions
, structAnnotations = compiledAnnotations , structAnnotations = compiledAnnotations
, structMemberMap = memberMap , structMemberMap = memberMap
, structMembersByNumber = Map.fromList $ mapMaybe memberByNumber members
, structMembers = members , structMembers = members
, structFieldPackingMap = fieldPackingMap , structFieldPackingMap = fieldPackingMap
}))) })))
......
...@@ -441,6 +441,8 @@ outerFileContext schemaNodes = fileContext where ...@@ -441,6 +441,8 @@ outerFileContext schemaNodes = fileContext where
unionContext parent desc = mkStrContext context where unionContext parent desc = mkStrContext context where
titleCase = toTitleCase $ unionName desc titleCase = toTitleCase $ unionName desc
unionIndex = Map.findIndex (unionNumber desc) $ structMembersByNumber $ unionParent desc
context "typeStruct" = MuBool False context "typeStruct" = MuBool False
context "typeUnion" = MuBool True context "typeUnion" = MuBool True
context "typeName" = MuVariable titleCase context "typeName" = MuVariable titleCase
...@@ -454,6 +456,7 @@ outerFileContext schemaNodes = fileContext where ...@@ -454,6 +456,7 @@ outerFileContext schemaNodes = fileContext where
context "unionTitleCase" = MuVariable titleCase context "unionTitleCase" = MuVariable titleCase
context "unionTagOffset" = MuVariable $ unionTagOffset desc context "unionTagOffset" = MuVariable $ unionTagOffset desc
context "unionFields" = MuList $ map (fieldContext context) $ unionFields desc context "unionFields" = MuList $ map (fieldContext context) $ unionFields desc
context "unionIndex" = MuVariable unionIndex
context s = parent s context s = parent s
childContext parent name = mkStrContext context where childContext parent name = mkStrContext context where
......
...@@ -444,6 +444,7 @@ data StructDesc = StructDesc ...@@ -444,6 +444,7 @@ data StructDesc = StructDesc
, structUnions :: [UnionDesc] , structUnions :: [UnionDesc]
, structAnnotations :: AnnotationMap , structAnnotations :: AnnotationMap
, structMemberMap :: MemberMap , structMemberMap :: MemberMap
, structMembersByNumber :: Map.Map Integer Desc -- top-level members only
, structMembers :: [Desc] , structMembers :: [Desc]
-- Don't use this directly, use the members of FieldDesc and UnionDesc. -- Don't use this directly, use the members of FieldDesc and UnionDesc.
......
...@@ -119,6 +119,11 @@ CAPNPROTO_DECLARE_STRUCT( ...@@ -119,6 +119,11 @@ CAPNPROTO_DECLARE_STRUCT(
CAPNPROTO_DECLARE_ENUM( CAPNPROTO_DECLARE_ENUM(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}}, {{enumId}}); ::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}}, {{enumId}});
{{/structNestedEnums}} {{/structNestedEnums}}
{{#structUnions}}
CAPNPROTO_DECLARE_UNION(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{structFullName}}::{{unionTitleCase}},
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{structFullName}}, {{unionIndex}});
{{/structUnions}}
{{/typeStruct}} {{/typeStruct}}
{{/typeStructOrUnion}} {{/typeStructOrUnion}}
{{/fileTypes}} {{/fileTypes}}
...@@ -145,9 +150,6 @@ public: ...@@ -145,9 +150,6 @@ public:
inline explicit Reader(::capnproto::internal::StructReader base): _reader(base) {} inline explicit Reader(::capnproto::internal::StructReader base): _reader(base) {}
{{#typeStruct}} {{#typeStruct}}
inline ::kj::String debugString() {
return ::capnproto::internal::debugString<{{typeName}}>(_reader);
}
inline size_t totalSizeInWords() { inline size_t totalSizeInWords() {
return _reader.totalSize() / ::capnproto::WORDS; return _reader.totalSize() / ::capnproto::WORDS;
} }
...@@ -186,7 +188,18 @@ private: ...@@ -186,7 +188,18 @@ private:
template <typename T, ::capnproto::Kind k> template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::internal::PointerHelpers; friend struct ::capnproto::internal::PointerHelpers;
friend class ::capnproto::MessageBuilder; friend class ::capnproto::MessageBuilder;
friend ::kj::String KJ_STRINGIFY({{typeFullName}}::Reader reader);
}; };
inline ::kj::String KJ_STRINGIFY({{typeFullName}}::Reader reader) {
{{#typeStruct}}
return ::capnproto::internal::structString<{{typeFullName}}>(reader._reader);
{{/typeStruct}}
{{#typeUnion}}
return ::capnproto::internal::unionString<{{typeFullName}}>(reader._reader);
{{/typeUnion}}
}
{{! ------------------------------------------------------------------------------------------- }} {{! ------------------------------------------------------------------------------------------- }}
class {{typeFullName}}::Builder { class {{typeFullName}}::Builder {
...@@ -199,7 +212,6 @@ public: ...@@ -199,7 +212,6 @@ public:
inline Reader asReader() { return *this; } inline Reader asReader() { return *this; }
{{#typeStruct}} {{#typeStruct}}
inline ::kj::String debugString() { return asReader().debugString(); }
inline size_t totalSizeInWords() { return asReader().totalSizeInWords(); } inline size_t totalSizeInWords() { return asReader().totalSizeInWords(); }
{{#structUnions}} {{#structUnions}}
...@@ -250,7 +262,17 @@ private: ...@@ -250,7 +262,17 @@ private:
::capnproto::internal::StructBuilder _builder; ::capnproto::internal::StructBuilder _builder;
template <typename T, ::capnproto::Kind k> template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::ToDynamic_; friend struct ::capnproto::ToDynamic_;
friend ::kj::String KJ_STRINGIFY({{typeFullName}}::Builder builder);
}; };
inline ::kj::String KJ_STRINGIFY({{typeFullName}}::Builder builder) {
{{#typeStruct}}
return ::capnproto::internal::structString<{{typeFullName}}>(builder._builder.asReader());
{{/typeStruct}}
{{#typeUnion}}
return ::capnproto::internal::unionString<{{typeFullName}}>(builder._builder.asReader());
{{/typeUnion}}
}
{{/typeStructOrUnion}} {{/typeStructOrUnion}}
{{/fileTypes}} {{/fileTypes}}
{{! =========================================================================================== }} {{! =========================================================================================== }}
......
...@@ -85,6 +85,10 @@ CAPNPROTO_DEFINE_STRUCT( ...@@ -85,6 +85,10 @@ CAPNPROTO_DEFINE_STRUCT(
CAPNPROTO_DEFINE_ENUM( CAPNPROTO_DEFINE_ENUM(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}}); ::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}});
{{/structNestedEnums}} {{/structNestedEnums}}
{{#structUnions}}
CAPNPROTO_DEFINE_UNION(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{structFullName}}::{{unionTitleCase}});
{{/structUnions}}
{{/typeStruct}} {{/typeStruct}}
{{/typeStructOrUnion}} {{/typeStructOrUnion}}
{{/fileTypes}} {{/fileTypes}}
......
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