// 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" #include "../diagnostics.hpp" namespace tmwa { namespace sexpr { template constexpr size_t not_negative_one() { return v; } template<> constexpr size_t not_negative_one(-1)>() = delete; 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 not_negative_one::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 = not_negative_one::template index()>(); } catch (...) { // TODO switch from requiring nothrow default construct, to // instead require nothrow moves, and offer the strong exception // guarantee (which is actually easier that the basic one) #if GCC != 407 // apparent compiler bug, not reduced // 4.7.2 from wheezy is bad // 4.7.3 from jessie is good // 4.7.3 from ubuntu-toolchain-test is bad static_assert(std::is_nothrow_constructible::value, "first element is nothrow constructible"); #endif 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(&data), 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 == not_negative_one::template index()>()) return data.template get(); return nullptr; } template template const E *Variant::get_if() const { if (state == not_negative_one::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