From 63c2e854ed2c9778ea9225d86578b4e9b65900ee Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Fri, 27 Jun 2014 18:10:48 -0700 Subject: Port the Variant junk --- src/sexpr/bind.cpp | 29 +++++ src/sexpr/bind.hpp | 51 ++++++++ src/sexpr/lexer.hpp | 2 + src/sexpr/parser.hpp | 2 + src/sexpr/union.cpp | 44 +++++++ src/sexpr/union.hpp | 108 +++++++++++++++++ src/sexpr/variant.cpp | 40 +++++++ src/sexpr/variant.hpp | 119 ++++++++++++++++++ src/sexpr/variant.tcc | 293 +++++++++++++++++++++++++++++++++++++++++++++ src/sexpr/variant_test.cpp | 125 +++++++++++++++++++ src/sexpr/void.cpp | 29 +++++ src/sexpr/void.hpp | 43 +++++++ 12 files changed, 885 insertions(+) create mode 100644 src/sexpr/bind.cpp create mode 100644 src/sexpr/bind.hpp create mode 100644 src/sexpr/union.cpp create mode 100644 src/sexpr/union.hpp create mode 100644 src/sexpr/variant.cpp create mode 100644 src/sexpr/variant.hpp create mode 100644 src/sexpr/variant.tcc create mode 100644 src/sexpr/variant_test.cpp create mode 100644 src/sexpr/void.cpp create mode 100644 src/sexpr/void.hpp (limited to 'src/sexpr') diff --git a/src/sexpr/bind.cpp b/src/sexpr/bind.cpp new file mode 100644 index 0000000..d8d0caa --- /dev/null +++ b/src/sexpr/bind.cpp @@ -0,0 +1,29 @@ +#include "bind.hpp" +// bind.cpp - Just include the header file. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace sexpr +{ +} // namespace sexpr +} // namespace tmwa diff --git a/src/sexpr/bind.hpp b/src/sexpr/bind.hpp new file mode 100644 index 0000000..7250f9d --- /dev/null +++ b/src/sexpr/bind.hpp @@ -0,0 +1,51 @@ +#ifndef TMWA_SEXPR_BIND_HPP +#define TMWA_SEXPR_BIND_HPP +// bind.hpp - Like std::bind, but with arbitrary arguments. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include + +# include "fwd.hpp" + + +namespace tmwa +{ +namespace sexpr +{ + template + struct VariadicBind + { + // note: may be lvalue references + F&& f; + T&& t; + template + auto operator()(A&&... a) -> decltype(std::forward(f)(std::forward(t), std::forward(a)...)) + { + return std::forward(f)(std::forward(t), std::forward(a)...); + } + }; + template + VariadicBind bind_variadic(F&& func, T&& arg1) + { + return VariadicBind{std::forward(func), std::forward(arg1)}; + } +} // namespace sexpr +} // namespace tmwa + +#endif //TMWA_SEXPR_VARIANT_HPP diff --git a/src/sexpr/lexer.hpp b/src/sexpr/lexer.hpp index d1c4643..bd7b532 100644 --- a/src/sexpr/lexer.hpp +++ b/src/sexpr/lexer.hpp @@ -29,6 +29,8 @@ # include "../io/line.hpp" +# include "fwd.hpp" + namespace tmwa { diff --git a/src/sexpr/parser.hpp b/src/sexpr/parser.hpp index 2404a9a..c787e56 100644 --- a/src/sexpr/parser.hpp +++ b/src/sexpr/parser.hpp @@ -29,6 +29,8 @@ # include "lexer.hpp" +# include "fwd.hpp" + namespace tmwa { diff --git a/src/sexpr/union.cpp b/src/sexpr/union.cpp new file mode 100644 index 0000000..6f65012 --- /dev/null +++ b/src/sexpr/union.cpp @@ -0,0 +1,44 @@ +#include "union.hpp" +// union.cpp - Just include the header file and try to instantiate. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace sexpr +{ +namespace +{ + struct Foo + { + Foo(); + Foo(const Foo&); + Foo& operator = (const Foo&); + ~Foo(); + }; +} // anonymous namespace +static Union u; + +static_assert(u.index() == 0, "int"); +static_assert(u.index() == 1, "Foo"); +static_assert(u.index() == size_t(-1), "char"); +} // namespace sexpr +} // namespace tmwa diff --git a/src/sexpr/union.hpp b/src/sexpr/union.hpp new file mode 100644 index 0000000..e0c3051 --- /dev/null +++ b/src/sexpr/union.hpp @@ -0,0 +1,108 @@ +#ifndef TMWA_SEXPR_UNION_HPP +#define TMWA_SEXPR_UNION_HPP +// union.hpp - A (unsafe!) convenience wrapper for classes in unions. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include +# include +# include +# include + +# include "fwd.hpp" + + +namespace tmwa +{ +namespace sexpr +{ + template + class Union; + + template<> + class Union<> + { + public: + template + constexpr static size_t index() + { + return -1; + } + Union() = default; + ~Union() = default; + Union(const Union&) = delete; + Union& operator = (const Union&) = delete; + }; + + template + class Union + { + static_assert(!std::is_const::value, "union elements are not const"); + static_assert(!std::is_volatile::value, "union elements are not volatile"); + static_assert(!std::is_reference::value, "union elements are not references"); + static_assert(Union::template index() == size_t(-1), "unions do not contain duplicates"); + union Impl + { + F first; + Union rest; + + Impl() {} + ~Impl() {} + } data; + public: + template + constexpr static size_t index() + { + return std::is_same::value + ? 0 + : 1 + Union::template index() + ?: -1; + } + template + void get(T*& p) { data.rest.get(p); } + void get(F*& p) { p = std::addressof(data.first); } + template + void get(const T*& p) const { data.rest.get(p); } + void get(const F*& p) const { p = std::addressof(data.first); } + + template + T *get() { T *out; get(out); return out; } + template + const T *get() const { const T *out; get(out); return out; } + + Union() = default; + ~Union() = default; + Union(const Union&) = delete; + Union& operator = (const Union&) = delete; + + template + void construct(A&&... a) + { + new (get()) T(std::forward(a)...); + } + + template + void destruct() + { + get()->~T(); + } + }; +} // namespace sexpr +} // namespace tmwa + +#endif //TMWA_SEXPR_UNION_HPP diff --git a/src/sexpr/variant.cpp b/src/sexpr/variant.cpp new file mode 100644 index 0000000..b1f500a --- /dev/null +++ b/src/sexpr/variant.cpp @@ -0,0 +1,40 @@ +#include "variant.hpp" +// variant.cpp - Just include the header file. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace sexpr +{ +namespace +{ + struct Foo + { + Foo() {} + ~Foo() {} + Foo(Foo&&) {} + Foo& operator = (Foo&&) { return *this; } + }; +} // anonymous namespace + static Variant v; +} // namespace sexpr +} // namespace tmwa diff --git a/src/sexpr/variant.hpp b/src/sexpr/variant.hpp new file mode 100644 index 0000000..ccbfeaa --- /dev/null +++ b/src/sexpr/variant.hpp @@ -0,0 +1,119 @@ +#ifndef TMWA_SEXPR_VARIANT_HPP +#define TMWA_SEXPR_VARIANT_HPP +// variant.hpp - A single value, multiple type container. Better than boost's. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include +# include +# include +# include + +# include "union.hpp" +# include "void.hpp" + +# include "fwd.hpp" + + +namespace tmwa +{ +namespace sexpr +{ +# define JOIN(a, b) a##b + +# define WITH_VAR(ty, var, expr) \ + for (bool JOIN(var, _guard) = true; JOIN(var, _guard); ) \ + for (ty var = expr; JOIN(var, _guard); JOIN(var, _guard) = false) +# define MATCH(expr) \ + WITH_VAR(auto&&, _match_var, expr) \ + switch (tmwa::sexpr::VariantFriend::get_state(_match_var)) +# define CASE(ty, var) \ + break; \ + case tmwa::sexpr::VariantFriend::get_state_for(): \ + WITH_VAR(ty, var, tmwa::sexpr::VariantFriend::unchecked_get(_match_var)) + + template + class Variant + { + static_assert(sizeof...(T), "A variant must not be empty"); + }; + template + class Variant + { + constexpr static size_t state_count = 1 + sizeof...(T); + + // simplify things immensely + friend class VariantFriend; + + typedef Union DataType; + DataType data; + size_t state; + + void do_destruct(); + template + void do_construct(A&&... a); + public: + Variant(); + ~Variant(); + + void reset(); + template + void emplace(A&&... a); + + Variant(const Variant& r); + Variant(Variant&& r); + Variant& operator = (const Variant& r); + Variant& operator = (Variant&& r); + + template + Variant(E e) + { + do_construct(std::move(e)); + } + + template + Variant& operator = (E e) + { + emplace(std::move(e)); + return *this; + } + + // use these ONLY if only one type makes sense + // otherwise use apply + template + bool is() const; + + template + E *get_if(); + + template + const E *get_if() const; + }; + + template + void apply(R& r, F&& f); + template + void apply(R& r, F&& f, V1&& v1, V&&... v); + template + void apply(Void&& r, F&& f, V&&... v); +} // namespace sexpr +} // namespace tmwa + +# include "variant.tcc" + +#endif //TMWA_SEXPR_VARIANT_HPP diff --git a/src/sexpr/variant.tcc b/src/sexpr/variant.tcc new file mode 100644 index 0000000..424a8f1 --- /dev/null +++ b/src/sexpr/variant.tcc @@ -0,0 +1,293 @@ +// variant.tcc - implementation of inlines and templates in variant.hpp +// +// Copyright © 2012-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include "bind.hpp" + +namespace tmwa +{ +namespace sexpr +{ + class VariantFriend + { + public: + template + static void unchecked_do_construct(Variant *var, A&&... a) + { + var->template do_construct(std::forward(a)...); + } + template + static E& unchecked_get(Variant& var) + { + return *var.data.template get(); + } + template + static const E& unchecked_get(const Variant& var) + { + return *var.data.template get(); + } + template + static E&& unchecked_get(Variant&& var) + { + return std::move(*var.data.template get()); + } + template + static const E&& unchecked_get(const Variant&& var) + { + return std::move(*var.data.template get()); + } + + template + static void _apply_unchecked(R& r, F&& f, V1&& v1, V&&... v) + { + apply(r, bind_variadic(std::forward(f), VariantFriend::unchecked_get(std::forward(v1))), std::forward(v)...); + } + + template + static void _apply_dispatch(const Variant *, R& r, F&& f, V1&& v1, V&&... v) + { + typedef void (*Function)(R&, F&&, V1&&, V&&...); + constexpr static Function dispatch[sizeof...(T)] = { _apply_unchecked... }; + assert(v1.state < sizeof...(T)); + dispatch[v1.state](r, std::forward(f), std::forward(v1), std::forward(v)...); + } + + template + static size_t get_state(const Variant& variant) + { + return variant.state; + } + + template + constexpr static size_t get_state_for() + { + return std::remove_reference::type::DataType::template index(); + } + }; + + + struct Destruct + { + template + void operator ()(U& v) + { + v.~U(); + } + }; + + template + void Variant::do_destruct() + { + apply(Void(), Destruct(), *this); + } + + template + template + void Variant::do_construct(A&&... a) + { + try + { + data.template construct(std::forward(a)...); + state = Union::template index(); + } + catch(...) + { + static_assert(std::is_nothrow_constructible::value, "first element is nothrow constructible"); + data.template construct(); + state = 0; + throw; + } + } + + template + Variant::Variant() + { + do_construct(); + state = 0; + } + + template + Variant::~Variant() + { + do_destruct(); + } + + template + void Variant::reset() + { + do_destruct(); + do_construct(); + } + + template + template + void Variant::emplace(A&&... a) + { + do_destruct(); + do_construct(std::forward(a)...); + } + + template + class CopyConstruct + { + Variant *target; + public: + CopyConstruct(Variant *v) : target(v) {} + template + void operator ()(const U& u) + { + VariantFriend::unchecked_do_construct(target, u); + } + }; + template + class MoveConstruct + { + Variant *target; + public: + MoveConstruct(Variant *v) : target(v) {} + template + void operator ()(U&& u) + { + VariantFriend::unchecked_do_construct(target, std::move(u)); + } + }; + + // assignment requires unchecked access + template + class CopyAssign + { + Union *data; + public: + CopyAssign(Union *d) : data(d) {} + template + void operator ()(const U& u) + { + *data->template get() = u; + } + }; + + template + class MoveAssign + { + Union *data; + public: + MoveAssign(Union *d) : data(d) {} + template + void operator () (U&& u) + { + *data->template get() = std::move(u); + } + }; + + template + Variant::Variant(const Variant& r) + { + apply(Void(), CopyConstruct(this), r); + } + + template + Variant::Variant(Variant&& r) + { + apply(Void(), MoveConstruct(this), std::move(r)); + } + + template + Variant& Variant::operator = (const Variant& r) + { + if (state == r.state) + apply(Void(), CopyAssign(this), r); + else + { + do_destruct(); + apply(Void(), CopyConstruct(this), r); + } + return *this; + } + + template + Variant& Variant::operator = (Variant&& r) + { + if (state == r.state) + apply(Void(), MoveAssign(&data), std::move(r)); + else + { + do_destruct(); + apply(Void(), MoveConstruct(this), std::move(r)); + } + return *this; + } + + template + template + bool Variant::is() const + { + return get_if(); + } + + template + template + E *Variant::get_if() + { + if (state == Union::template index()) + return data.template get(); + return nullptr; + } + + template + template + const E *Variant::get_if() const + { + if (state == Union::template index()) + return data.template get(); + return nullptr; + } + + template + void _apply_assign(std::true_type, R& r, F&& f) + { + std::forward(f)(); + r = Void(); + } + + template + void _apply_assign(std::false_type, R& r, F&& f) + { + r = std::forward(f)(); + } + + template + void apply(R& r, F&& f) + { + _apply_assign(std::is_void(f)())>(), r, std::forward(f)); + } + + template + void apply(R& r, F&& f, V1&& v1, V&&... v) + { + VariantFriend::_apply_dispatch(&v1, r, std::forward(f), std::forward(v1), std::forward(v)...); + } + + template + void apply(Void&& r, F&& f, V&&... v) + { + apply(r, std::forward(f), std::forward(v)...); + } + +} // namespace sexpr +} // namespace tmwa diff --git a/src/sexpr/variant_test.cpp b/src/sexpr/variant_test.cpp new file mode 100644 index 0000000..1ecb82f --- /dev/null +++ b/src/sexpr/variant_test.cpp @@ -0,0 +1,125 @@ +#include "variant.hpp" +// variant_test.cpp - Testsuite for multi-type container that's better than boost's. +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../strings/vstring.hpp" + +//#include "../poison.hpp" + + +namespace tmwa +{ +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + + struct Tracker + { + int id, moves, copies; + + explicit + Tracker(int i) + : id(i), moves(0), copies(0) + {} + Tracker(Tracker&& rhs) + : id(rhs.id), moves(rhs.moves + 1), copies(rhs.copies) + { rhs.id = 0; } + Tracker(const Tracker& rhs) + : id(rhs.id), moves(rhs.moves), copies(rhs.copies + 1) + {} + Tracker& operator = (Tracker&& rhs) + { id = rhs.id; moves = rhs.moves + 1; copies = rhs.copies; rhs.id = 0; return *this; } + Tracker& operator = (const Tracker& rhs) + { id = rhs.id; moves = rhs.moves; copies = rhs.copies + 1; return *this; } + }; + struct Foo : Tracker + { + // needed for first param of variant + Foo() noexcept : Tracker(0) { abort(); } + + Foo(int i) : Tracker(i) {} + }; + struct Bar : Tracker + { + Bar(int i) : Tracker(i) {} + }; + struct Qux : Tracker + { + // needed for first param of variant + Qux() noexcept : Tracker(0) { abort(); } + + Qux(int i) : Tracker(i) {} + Qux(Qux&&) = default; + Qux(const Qux&) = delete; + Qux& operator = (Qux&&) = default; + Qux& operator = (const Qux&) = default; + }; + +TEST(variant, match) +{ + struct Sub : sexpr::Variant + { + Sub() + : sexpr::Variant(Foo(1)) + {} + }; + Sub v1; + MATCH (v1) + { + // This is not a public API, it's just for testing. + default: + FAIL(); + + CASE(Foo, f) + { + (void)f; + SUCCEED(); + } + CASE(Bar, b) + { + (void)b; + FAIL(); + } + } + v1.emplace(2); + MATCH (v1) + { + // This is not a public API, it's just for testing. + default: + FAIL(); + + CASE(Foo, f) + { + (void)f; + FAIL(); + } + CASE(Bar, b) + { + (void)b; + SUCCEED(); + } + } +} + +TEST(variant, copymove) +{ + sexpr::Variant moveonly(Qux(3)); + (void)moveonly; +} +} // namespace tmwa diff --git a/src/sexpr/void.cpp b/src/sexpr/void.cpp new file mode 100644 index 0000000..9f0eeb5 --- /dev/null +++ b/src/sexpr/void.cpp @@ -0,0 +1,29 @@ +#include "void.hpp" +// void.cpp - Just include the header file. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" + + +namespace tmwa +{ +namespace sexpr +{ +} // namespace sexpr +} // namespace tmwa diff --git a/src/sexpr/void.hpp b/src/sexpr/void.hpp new file mode 100644 index 0000000..9fb6bc4 --- /dev/null +++ b/src/sexpr/void.hpp @@ -0,0 +1,43 @@ +#ifndef TMWA_SEXPR_VOID_HPP +#define TMWA_SEXPR_VOID_HPP +// void.hpp - A type that represents nothing and anything. +// +// Copyright © 2012 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "fwd.hpp" + + +namespace tmwa +{ +namespace sexpr +{ + struct Void + { + template + constexpr operator T() noexcept { return T(); } + template + void operator = (T&&) noexcept {} + template + constexpr Void(T&&) noexcept {} + constexpr Void() noexcept; + }; + constexpr Void::Void() noexcept = default; +} // namespace sexpr +} // namespace tmwa + +#endif //TMWA_SEXPR_VOID_HPP -- cgit v1.2.3-70-g09d2