Commit de7f266a authored by Kenton Varda's avatar Kenton Varda

Add utility code for really nice command-line option parsing and start using it…

Add utility code for really nice command-line option parsing and start using it in the compiler (still WIP).
parent b92a28a6
......@@ -110,8 +110,10 @@ includekj_HEADERS = \
src/kj/arena.h \
src/kj/io.h \
src/kj/tuple.h \
src/kj/function.h \
src/kj/mutex.h \
src/kj/thread.h
src/kj/thread.h \
src/kj/main.h
includekjparse_HEADERS = \
src/kj/parse/common.h \
......@@ -150,6 +152,7 @@ libcapnp_la_SOURCES= \
src/kj/io.c++ \
src/kj/mutex.c++ \
src/kj/thread.c++ \
src/kj/main.c++ \
src/kj/parse/char.c++ \
src/capnp/blob.c++ \
src/capnp/arena.h \
......@@ -199,6 +202,7 @@ capnp_test_SOURCES = \
src/kj/arena-test.c++ \
src/kj/units-test.c++ \
src/kj/tuple-test.c++ \
src/kj/function-test.c++ \
src/kj/mutex-test.c++ \
src/kj/parse/common-test.c++ \
src/kj/parse/char-test.c++ \
......
......@@ -24,6 +24,7 @@
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include "module-loader.h"
#include <capnp/pretty-print.h>
#include <kj/vector.h>
#include <kj/io.h>
......@@ -31,6 +32,10 @@
#include <kj/debug.h>
#include "../message.h"
#include <iostream>
#include <kj/main.h>
namespace capnp {
namespace compiler {
class DummyModule: public capnp::compiler::Module {
public:
......@@ -53,6 +58,36 @@ public:
}
};
class CompilerMain {
public:
explicit CompilerMain(kj::ProcessContext& context)
: context(context), loader(STDERR_FILENO) {}
kj::MainFunc getMain() {
return kj::MainBuilder(
context, "Cap'n Proto compiler version 0.2",
"Compiles Cap'n Proto schema files and generates corresponding source code in one or "
"more languages.")
.expectOneOrMoreArgs("source", KJ_BIND_METHOD(*this, addSource))
.build();
}
kj::MainBuilder::Validity addSource(kj::StringPtr file) {
KJ_IF_MAYBE(module, loader.loadModule(file, file)) {
compiler.add(*module, Compiler::EAGER);
} else {
return "no such file";
}
return true;
}
private:
kj::ProcessContext& context;
ModuleLoader loader;
Compiler compiler;
};
int main(int argc, char* argv[]) {
// Eventually this will be capnpc. For now it's just a dummy program that tests parsing.
......@@ -109,3 +144,8 @@ int main(int argc, char* argv[]) {
return 0;
}
} // namespace compiler
} // namespace capnp
KJ_MAIN(capnp::compiler::CompilerMain);
......@@ -81,7 +81,7 @@ public:
kj::Maybe<const Node&> lookup(const DeclName::Reader& name) const;
// Resolve an arbitrary DeclName to a Node.
kj::Maybe<Schema> getBootstrapOrFinalSchema() const;
kj::Maybe<Schema> getBootstrapSchema() const;
kj::Maybe<Schema> getFinalSchema() const;
void traverse(Compiler::Mode mode) const;
......@@ -93,7 +93,7 @@ public:
// implements NodeTranslator::Resolver -----------------------------
kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) const override;
kj::Maybe<Schema> resolveMaybeBootstrapSchema(uint64_t id) const override;
kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) const override;
kj::Maybe<Schema> resolveFinalSchema(uint64_t id) const override;
private:
......@@ -212,7 +212,7 @@ public:
Impl();
virtual ~Impl();
uint64_t add(Module& module, Mode mode) const;
uint64_t add(const Module& module, Mode mode) const;
const CompiledModule& add(const Module& parsedModule) const;
struct Workspace {
......@@ -473,8 +473,8 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
locked->translator->getBootstrapNode());
})) {
locked->bootstrapSchema = nullptr;
addError(kj::str("Internal compiler bug: Bootstrap schema failed validation: ",
exception->getDescription()));
addError(kj::str("Internal compiler bug: Bootstrap schema failed validation:\n",
*exception));
}
// If the Workspace is destroyed while this Node is still in the BOOTSTRAP state,
......@@ -500,8 +500,8 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
locked->translator->finish());
})) {
locked->finalSchema = nullptr;
addError(kj::str("Internal compiler bug: Schema failed validation: ",
exception->getDescription()));
addError(kj::str("Internal compiler bug: Schema failed validation:\n",
*exception));
}
locked->advanceState(Content::FINISHED);
......@@ -605,12 +605,19 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
return *node;
}
kj::Maybe<Schema> Compiler::Node::getBootstrapOrFinalSchema() const {
kj::Maybe<Schema> Compiler::Node::getBootstrapSchema() const {
auto& content = getContent(Content::BOOTSTRAP);
if (__atomic_load_n(&content.state, __ATOMIC_ACQUIRE) == Content::FINISHED &&
content.finalSchema != nullptr) {
return content.finalSchema;
content.bootstrapSchema == nullptr) {
// The bootstrap schema was discarded. Copy it from the final schema.
// (We can't just return the final schema because using it could trigger schema loader
// callbacks that would deadlock.)
KJ_IF_MAYBE(finalSchema, content.finalSchema) {
return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(finalSchema->getProto());
} else {
return nullptr;
}
} else {
return content.bootstrapSchema;
}
......@@ -640,9 +647,9 @@ kj::Maybe<NodeTranslator::Resolver::ResolvedName> Compiler::Node::resolve(
});
}
kj::Maybe<Schema> Compiler::Node::resolveMaybeBootstrapSchema(uint64_t id) const {
kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(uint64_t id) const {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
return node->getBootstrapOrFinalSchema();
return node->getBootstrapSchema();
} else {
KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
}
......@@ -743,7 +750,7 @@ kj::Maybe<const Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr nam
}
}
uint64_t Compiler::Impl::add(Module& module, Mode mode) const {
uint64_t Compiler::Impl::add(const Module& module, Mode mode) const {
Impl::Workspace workspace;
auto lock = this->workspace.lockExclusive();
*lock = &workspace;
......@@ -766,7 +773,7 @@ void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
node->getFinalSchema();
} else {
// Must be the bootstrap loader.
node->getBootstrapOrFinalSchema();
node->getBootstrapSchema();
}
}
}
......@@ -776,7 +783,7 @@ void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
Compiler::Compiler(): impl(kj::heap<Impl>()) {}
Compiler::~Compiler() {}
uint64_t Compiler::add(Module& module, Mode mode) const {
uint64_t Compiler::add(const Module& module, Mode mode) const {
return impl->add(module, mode);
}
......
......@@ -76,7 +76,7 @@ public:
// use EAGER mode.
};
uint64_t add(Module& module, Mode mode) const;
uint64_t add(const Module& module, Mode mode) const;
// Add a module to the Compiler, returning the module's file ID. The ID can then be used to
// look up the schema in the SchemaLoader returned by `getLoader()`. However, if there were any
// errors while compiling (reported via `module.addError()`), then the SchemaLoader may behave as
......
......@@ -263,7 +263,7 @@ public:
uint startLine = findLargestElementBefore(lines, startByte);
uint startCol = startByte - lines[startLine];
loader.writeError(
kj::str(localName, ":", startLine, ":", startCol, ": error: ", message, "\n"));
kj::str(localName, ":", startLine + 1, ":", startCol + 1, ": error: ", message, "\n"));
}
private:
......@@ -291,7 +291,7 @@ kj::Maybe<const Module&> ModuleLoader::Impl::loadModule(
return *iter->second;
}
if (access(canonicalLocalName.cStr(), R_OK) < 0) {
if (access(canonicalLocalName.cStr(), F_OK) < 0) {
// No such file.
return nullptr;
}
......
......@@ -1489,8 +1489,7 @@ void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::
}
break;
case schema::Type::Body::STRUCT_TYPE:
KJ_IF_MAYBE(structSchema, resolver.resolveMaybeBootstrapSchema(
type.getBody().getStructType())) {
KJ_IF_MAYBE(structSchema, resolver.resolveBootstrapSchema(type.getBody().getStructType())) {
DynamicSlot slot(valueUnion, member, structSchema->asStruct());
compileValue(source, slot, isBootstrap);
}
......@@ -1527,7 +1526,7 @@ void NodeTranslator::compileValueInner(
kj::StringPtr id = name.getBase().getRelativeName().getValue();
KJ_IF_MAYBE(enumId, dst.getEnumType()) {
KJ_IF_MAYBE(enumSchema, resolver.resolveMaybeBootstrapSchema(*enumId)) {
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(*enumId)) {
KJ_IF_MAYBE(enumerant, enumSchema->asEnum().findEnumerantByName(id)) {
dst.set(DynamicEnum(*enumerant));
wasSet = true;
......@@ -1677,7 +1676,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
// We need to be very careful not to query this Schema's dependencies because if it is
// a final schema then this query could trigger a lazy load which would deadlock.
kj::Maybe<Schema> maybeConstSchema = isBootstrap ?
resolver.resolveMaybeBootstrapSchema(resolved->id) :
resolver.resolveBootstrapSchema(resolved->id) :
resolver.resolveFinalSchema(resolved->id);
KJ_IF_MAYBE(constSchema, maybeConstSchema) {
auto constReader = constSchema->getProto().getBody().getConstNode();
......@@ -1689,7 +1688,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
auto constType = constReader.getType();
switch (constType.getBody().which()) {
case schema::Type::Body::STRUCT_TYPE:
KJ_IF_MAYBE(structSchema, resolver.resolveMaybeBootstrapSchema(
KJ_IF_MAYBE(structSchema, resolver.resolveBootstrapSchema(
constType.getBody().getStructType())) {
constValue = objValue.as(structSchema->asStruct());
} else {
......@@ -1719,7 +1718,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
// A fully unqualified identifier looks like it might refer to a constant visible in the
// current scope, but if that's really what the user wanted, we want them to use a
// qualified name to make it more obvious. Report an error.
KJ_IF_MAYBE(scope, resolver.resolveMaybeBootstrapSchema(
KJ_IF_MAYBE(scope, resolver.resolveBootstrapSchema(
constSchema->getProto().getScopeId())) {
auto scopeReader = scope->getProto();
kj::StringPtr parent;
......@@ -1752,19 +1751,19 @@ kj::Maybe<ListSchema> NodeTranslator::makeListSchemaOf(schema::Type::Reader elem
auto body = elementType.getBody();
switch (body.which()) {
case schema::Type::Body::ENUM_TYPE:
KJ_IF_MAYBE(enumSchema, resolver.resolveMaybeBootstrapSchema(body.getEnumType())) {
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(body.getEnumType())) {
return ListSchema::of(enumSchema->asEnum());
} else {
return nullptr;
}
case schema::Type::Body::STRUCT_TYPE:
KJ_IF_MAYBE(structSchema, resolver.resolveMaybeBootstrapSchema(body.getStructType())) {
KJ_IF_MAYBE(structSchema, resolver.resolveBootstrapSchema(body.getStructType())) {
return ListSchema::of(structSchema->asStruct());
} else {
return nullptr;
}
case schema::Type::Body::INTERFACE_TYPE:
KJ_IF_MAYBE(interfaceSchema, resolver.resolveMaybeBootstrapSchema(body.getInterfaceType())) {
KJ_IF_MAYBE(interfaceSchema, resolver.resolveBootstrapSchema(body.getInterfaceType())) {
return ListSchema::of(interfaceSchema->asInterface());
} else {
return nullptr;
......@@ -1806,7 +1805,7 @@ Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
"'", declNameString(name), "' is not an annotation."));
} else {
annotationBuilder.setId(decl->id);
KJ_IF_MAYBE(annotationSchema, resolver.resolveMaybeBootstrapSchema(decl->id)) {
KJ_IF_MAYBE(annotationSchema, resolver.resolveBootstrapSchema(decl->id)) {
auto node = annotationSchema->getProto().getBody().getAnnotationNode();
if (!toDynamic(node).get(targetsFlagName).as<bool>()) {
errorReporter.addErrorOn(name, kj::str(
......
......@@ -54,17 +54,24 @@ public:
// Look up the given name, relative to this node, and return basic information about the
// target.
virtual kj::Maybe<Schema> resolveMaybeBootstrapSchema(uint64_t id) const = 0;
// Get the schema for the given ID. Returning either a bootstrap schema or a final schema
// is acceptable. Throws an exception if the id is not one that was found by calling resolve()
// or by traversing other schemas. Returns null if the ID is recognized, but the corresponding
virtual kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) const = 0;
// Get the schema for the given ID. If a schema is returned, it must be safe to traverse its
// dependencies using Schema::getDependency(). A schema that is only at the bootstrap stage
// is acceptable.
//
// Throws an exception if the id is not one that was found by calling resolve() or by
// traversing other schemas. Returns null if the ID is recognized, but the corresponding
// schema node failed to be built for reasons that were already reported.
virtual kj::Maybe<Schema> resolveFinalSchema(uint64_t id) const = 0;
// Get the final schema for the given ID. A bootstrap schema is not acceptable. Throws an
// exception if the id is not one that was found by calling resolve() or by traversing other
// schemas. Returns null if the ID is recognized, but the corresponding schema node failed to
// be built for reasons that were already reported.
// Get the final schema for the given ID. A bootstrap schema is not acceptable. It is NOT
// safe to traverse the schema's dependencies with Schema::getDependency() as doing so may
// trigger lazy loading callbacks that deadlock on the compiler mutex. Instead, the caller
// should carefully look up dependencies through this Resolver.
//
// Throws an exception if the id is not one that was found by calling resolve() or by
// traversing other schemas. Returns null if the ID is recognized, but the corresponding
// schema node failed to be built for reasons that were already reported.
};
NodeTranslator(const Resolver& resolver, const ErrorReporter& errorReporter,
......
......@@ -583,11 +583,23 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorRep
// -----------------------------------------------------------------
parsers.usingDecl = arena.copy(p::transform(
p::sequence(keyword("using"), identifier, op("="), parsers.declName),
[this](Located<Text::Reader>&& name, Orphan<DeclName>&& target) -> DeclParserResult {
p::sequence(keyword("using"), p::optional(p::sequence(identifier, op("="))),
parsers.declName),
[this](kj::Maybe<Located<Text::Reader>>&& name, Orphan<DeclName>&& target)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
name.copyTo(builder.initName());
KJ_IF_MAYBE(n, name) {
n->copyTo(builder.initName());
} else {
auto targetPath = target.getReader().getMemberPath();
if (targetPath.size() == 0) {
errorReporter.addErrorOn(
target.getReader(), "'using' declaration without '=' must use a qualified path.");
} else {
builder.setName(targetPath[targetPath.size() - 1]);
}
}
// no id, no annotations for using decl
builder.getBody().initUsingDecl().adoptTarget(kj::mv(target));
return DeclParserResult(kj::mv(decl));
......
......@@ -54,6 +54,12 @@ void DestructorOnlyArrayDisposer::disposeImpl(
}
}
const NullArrayDisposer NullArrayDisposer::instance = NullArrayDisposer();
void NullArrayDisposer::disposeImpl(
void* firstElement, size_t elementSize, size_t elementCount,
size_t capacity, void (*destroyElement)(void*)) const {}
namespace _ { // private
void* HeapArrayDisposer::allocateImpl(size_t elementSize, size_t elementCount, size_t capacity,
......
......@@ -108,6 +108,17 @@ public:
size_t capacity, void (*destroyElement)(void*)) const override;
};
class NullArrayDisposer: public ArrayDisposer {
// An ArrayDisposer that does nothing. Can be used to construct a fake Arrays that doesn't
// actually own its content.
public:
static const NullArrayDisposer instance;
void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
size_t capacity, void (*destroyElement)(void*)) const override;
};
// =======================================================================================
// Array
......
// 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 "function.h"
#include <gtest/gtest.h>
namespace kj {
namespace {
TEST(Function, Lambda) {
int i = 0;
Function<int(int, int)> f = [&](int a, int b) { return a + b + i++; };
EXPECT_EQ(123 + 456, f(123, 456));
EXPECT_EQ(7 + 8 + 1, f(7, 8));
EXPECT_EQ(9 + 2 + 2, f(2, 9));
EXPECT_EQ(i, 3);
}
struct TestType {
int callCount;
TestType(int callCount = 0): callCount(callCount) {}
~TestType() { callCount = 1234; }
// Make sure we catch invalid post-destruction uses.
int foo(int a, int b) {
return a + b + callCount++;
}
};
TEST(Function, Method) {
TestType obj;
Function<int(int, int)> f = KJ_BIND_METHOD(obj, foo);
Function<uint(uint, uint)> f2 = KJ_BIND_METHOD(obj, foo);
EXPECT_EQ(123 + 456, f(123, 456));
EXPECT_EQ(7 + 8 + 1, f(7, 8));
EXPECT_EQ(9 + 2 + 2, f2(2, 9));
EXPECT_EQ(3, obj.callCount);
// Bind to a temporary.
f = KJ_BIND_METHOD(TestType(10), foo);
EXPECT_EQ(123 + 456 + 10, f(123, 456));
EXPECT_EQ(7 + 8 + 11, f(7, 8));
EXPECT_EQ(9 + 2 + 12, f(2, 9));
// Bind to a move.
f = KJ_BIND_METHOD(kj::mv(obj), foo);
obj.callCount = 1234;
EXPECT_EQ(123 + 456 + 3, f(123, 456));
EXPECT_EQ(7 + 8 + 4, f(7, 8));
EXPECT_EQ(9 + 2 + 5, f(2, 9));
}
} // namespace
} // namespace kj
// 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_FUNCTION_H_
#define KJ_FUNCTION_H_
#include "memory.h"
namespace kj {
template <typename Signature>
class Function;
// Function wrapper using virtual-based polymorphism. Use this when template polymorphism is
// not possible. You can, for example, accept a Function as a parameter:
//
// void setFilter(Function<bool(const Widget&)> filter);
//
// The caller of `setFilter()` may then pass any callable object as the parameter. The callable
// object does not have to have the exact signature specified, just one that is "compatible" --
// i.e. the return type is covariant and the parameters are contravariant.
//
// Unlike `std::function`, `kj::Function`s are movable but not copyable, just like `kj::Own`. This
// is to avoid unexpected heap allocation or slow atomic reference counting.
//
// When a `Function` is constructed from an lvalue, it captures only a reference to the value.
// When constructed from an rvalue, it invokes the value's move constructor. So, for example:
//
// struct AddN {
// int n;
// int operator(int i) { return i + n; }
// }
//
// Function<int(int, int)> f1 = AddN{2};
// // f1 owns an instance of AddN. It may safely be moved out
// // of the local scope.
//
// AddN adder(2);
// Function<int(int, int)> f2 = adder;
// // f2 contains a reference to `adder`. Thus, it becomes invalid
// // when `adder` goes out-of-scope.
//
// AddN adder2(2);
// Function<int(int, int)> f3 = kj::mv(adder2);
// // f3 owns an insatnce of AddN moved from `adder2`. f3 may safely
// // be moved out of the local scope.
//
// Additionally, a Function may be bound to a class method using KJ_BIND_METHOD(object, methodName).
// For example:
//
// class Printer {
// public:
// void print(int i);
// void print(kj::StringPtr s);
// };
//
// Printer p;
//
// Function<void(uint)> intPrinter = KJ_BIND_METHOD(p, print);
// // Will call Printer::print(int).
//
// Function<void(const char*)> strPrinter = KJ_BIND_METHOD(p, print);
// // Will call Printer::print(kj::StringPtr).
//
// Notice how KJ_BIND_METHOD is able to figure out which overload to use depending on the kind of
// Function it is binding to.
template <typename Return, typename... Params>
class Function<Return(Params...)> {
public:
template <typename F>
inline Function(F&& f): impl(heap<Impl<F>>(kj::fwd<F>(f))) {}
Function() = default;
inline Return operator()(Params... params) {
return (*impl)(kj::fwd<Params>(params)...);
}
private:
class Iface {
public:
virtual Return operator()(Params... params) = 0;
};
template <typename F>
class Impl final: public Iface {
public:
explicit Impl(F&& f): f(kj::fwd<F>(f)) {}
Return operator()(Params... params) override {
return f(kj::fwd<Params>(params)...);
}
private:
F f;
};
Own<Iface> impl;
};
namespace _ {
template <typename T>
T rvalueOrRef(T&&);
// Hack to help detect if an expression is an lvalue or an rvalue.
//
// int i;
// decltype(i) i1(i); // i1 has type int.
// decltype(rvalueOrRef(i)) i2(i); // i2 has type int&.
// decltype(rvalueOrRef(kj::mv(i)) i3(kj::mv(i)); // i3 has type int.
} // namespace _
#define KJ_BIND_METHOD(obj, method) \
({ \
typedef decltype(::kj::_::rvalueOrRef(obj)) T; \
class F { \
public: \
inline F(T&& t): t(::kj::fwd<T>(t)) {} \
template <typename... Params> \
auto operator()(Params&&... params) \
-> decltype(::kj::instance<T>().method(::kj::fwd<Params>(params)...)) { \
return t.method(::kj::fwd<Params>(params)...); \
} \
private: \
T t; \
}; \
(F(obj)); \
})
// Macro that produces a functor object which forwards to the method `obj.name`. If `obj` is an
// lvalue, the functor will hold a reference to it. If `obj` is an rvalue, the functor will
// contain a copy (by move) of it.
} // namespace kj
#endif // KJ_FUNCTION_H_
This diff is collapsed.
This diff is collapsed.
......@@ -84,6 +84,8 @@ public:
inline bool startsWith(const StringPtr& other) const;
inline bool endsWith(const StringPtr& other) const;
inline Maybe<size_t> findFirst(char c) const;
private:
inline StringPtr(ArrayPtr<const char> content): content(content) {}
......@@ -109,6 +111,8 @@ public:
inline String(decltype(nullptr)): content(nullptr) {}
inline String(char* value, size_t size, const ArrayDisposer& disposer);
// Does not copy. `size` does not include NUL terminator, but `value` must be NUL-terminated.
inline explicit String(Array<char> buffer);
// Does not copy. Requires `buffer` ends with `\0`.
inline operator ArrayPtr<char>();
inline operator ArrayPtr<const char>() const;
......@@ -373,6 +377,15 @@ inline bool StringPtr::endsWith(const StringPtr& other) const {
memcmp(end() - other.size(), other.content.begin(), other.size()) == 0;
}
inline Maybe<size_t> StringPtr::findFirst(char c) const {
const char* pos = reinterpret_cast<const char*>(memchr(content.begin(), c, size()));
if (pos == nullptr) {
return nullptr;
} else {
return pos - content.begin();
}
}
inline String::operator ArrayPtr<char>() {
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
}
......@@ -404,6 +417,10 @@ inline String::String(char* value, size_t size, const ArrayDisposer& disposer)
KJ_IREQUIRE(value[size] == '\0', "String must be NUL-terminated.");
}
inline String::String(Array<char> buffer): content(kj::mv(buffer)) {
KJ_IREQUIRE(content.size() > 0 && content.back() == '\0', "String must be NUL-terminated.");
}
inline String heapString(const char* value) {
return heapString(value, strlen(value));
}
......
......@@ -82,6 +82,11 @@ public:
builder.addAll(begin, end);
}
template <typename Container>
inline void addAll(Container&& container) {
addAll(container.begin(), container.end());
}
private:
ArrayBuilder<T> builder;
......
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