summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/optimizer/algebra/polyvalue.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/optimizer/algebra/polyvalue.h')
-rw-r--r--src/mongo/db/query/optimizer/algebra/polyvalue.h381
1 files changed, 381 insertions, 0 deletions
diff --git a/src/mongo/db/query/optimizer/algebra/polyvalue.h b/src/mongo/db/query/optimizer/algebra/polyvalue.h
new file mode 100644
index 00000000000..374041c5704
--- /dev/null
+++ b/src/mongo/db/query/optimizer/algebra/polyvalue.h
@@ -0,0 +1,381 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <array>
+#include <stdexcept>
+#include <type_traits>
+
+namespace mongo::optimizer {
+namespace algebra {
+namespace detail {
+
+template <typename T, typename... Args>
+inline constexpr bool is_one_of_v = std::disjunction_v<std::is_same<T, Args>...>;
+
+template <typename T, typename... Args>
+inline constexpr bool is_one_of_f() {
+ return is_one_of_v<T, Args...>;
+}
+
+template <typename... Args>
+struct is_unique_t : std::true_type {};
+
+template <typename H, typename... T>
+struct is_unique_t<H, T...>
+ : std::bool_constant<!is_one_of_f<H, T...>() && is_unique_t<T...>::value> {};
+
+template <typename... Args>
+inline constexpr bool is_unique_v = is_unique_t<Args...>::value;
+
+// Given the type T find its index in Ts
+template <typename T, typename... Ts>
+static inline constexpr int find_index() {
+ static_assert(detail::is_unique_v<Ts...>, "Types must be unique");
+ constexpr bool matchVector[] = {std::is_same<T, Ts>::value...};
+
+ for (int index = 0; index < static_cast<int>(sizeof...(Ts)); ++index) {
+ if (matchVector[index]) {
+ return index;
+ }
+ }
+
+ return -1;
+}
+
+template <int N, typename T, typename... Ts>
+struct get_type_by_index_impl {
+ using type = typename get_type_by_index_impl<N - 1, Ts...>::type;
+};
+template <typename T, typename... Ts>
+struct get_type_by_index_impl<0, T, Ts...> {
+ using type = T;
+};
+
+// Given the index I return the type from Ts
+template <int I, typename... Ts>
+using get_type_by_index = typename get_type_by_index_impl<I, Ts...>::type;
+
+} // namespace detail
+
+/*=====-----
+ *
+ * The overload trick to construct visitors from lambdas.
+ *
+ */
+template <class... Ts>
+struct overload : Ts... {
+ using Ts::operator()...;
+};
+template <class... Ts>
+overload(Ts...)->overload<Ts...>;
+
+/*=====-----
+ *
+ * Forward declarations
+ *
+ */
+template <typename... Ts>
+class PolyValue;
+
+template <typename T, typename... Ts>
+class ControlBlockVTable;
+
+/*=====-----
+ *
+ * The base control block that PolyValue holds.
+ *
+ * It does not contain anything else by the runtime tag.
+ *
+ */
+template <typename... Ts>
+class ControlBlock {
+ const int _tag;
+
+protected:
+ ControlBlock(int tag) noexcept : _tag(tag) {}
+
+public:
+ auto getRuntimeTag() const noexcept {
+ return _tag;
+ }
+};
+
+/*=====-----
+ *
+ * The concrete control block VTable generator.
+ *
+ * It must be empty ad PolyValue derives from the generators
+ * and we want EBO to kick in.
+ *
+ */
+template <typename T, typename... Ts>
+class ControlBlockVTable {
+ static constexpr int _staticTag = detail::find_index<T, Ts...>();
+ static_assert(_staticTag != -1, "Type must be on the list");
+
+ using AbstractType = ControlBlock<Ts...>;
+ using PolyValueType = PolyValue<Ts...>;
+
+ /*=====-----
+ *
+ * The concrete control block for every type T of Ts.
+ *
+ * It derives from the ControlBlock. All methods are private and only
+ * the friend class ControlBlockVTable can call them.
+ *
+ */
+ class ConcreteType : public AbstractType {
+ T _t;
+
+ public:
+ template <typename... Args>
+ ConcreteType(Args&&... args) : AbstractType(_staticTag), _t(std::forward<Args>(args)...) {}
+
+ const T* getPtr() const {
+ return &_t;
+ }
+
+ T* getPtr() {
+ return &_t;
+ }
+ };
+
+ static constexpr auto concrete(AbstractType* block) noexcept {
+ return static_cast<ConcreteType*>(block);
+ }
+
+ static constexpr auto concrete(const AbstractType* block) noexcept {
+ return static_cast<const ConcreteType*>(block);
+ }
+
+public:
+ template <typename... Args>
+ static AbstractType* make(Args&&... args) {
+ return new ConcreteType(std::forward<Args>(args)...);
+ }
+
+ static AbstractType* clone(const AbstractType* block) {
+ return new ConcreteType(*concrete(block));
+ }
+
+ static void destroy(AbstractType* block) noexcept {
+ delete concrete(block);
+ }
+
+ static bool compareEq(AbstractType* blockLhs, AbstractType* blockRhs) noexcept {
+ if (blockLhs->getRuntimeTag() == blockRhs->getRuntimeTag()) {
+ return *castConst<T>(blockLhs) == *castConst<T>(blockRhs);
+ }
+ return false;
+ }
+
+ template <typename U>
+ static constexpr bool is_v = std::is_base_of_v<U, T>;
+
+ template <typename U>
+ static U* cast(AbstractType* block) {
+ if constexpr (is_v<U>) {
+ return static_cast<U*>(concrete(block)->getPtr());
+ } else {
+ // gcc bug 81676
+ (void)block;
+ return nullptr;
+ }
+ }
+
+ template <typename U>
+ static const U* castConst(const AbstractType* block) {
+ if constexpr (is_v<U>) {
+ return static_cast<const U*>(concrete(block)->getPtr());
+ } else {
+ // gcc bug 81676
+ (void)block;
+ return nullptr;
+ }
+ }
+
+ template <typename V, typename... Args>
+ static auto visit(V&& v, PolyValueType& holder, AbstractType* block, Args&&... args) {
+ return v(holder, *cast<T>(block), std::forward<Args>(args)...);
+ }
+
+ template <typename V, typename... Args>
+ static auto visitConst(V&& v,
+ const PolyValueType& holder,
+ const AbstractType* block,
+ Args&&... args) {
+ return v(holder, *castConst<T>(block), std::forward<Args>(args)...);
+ }
+};
+
+/*=====-----
+ *
+ * This is a variation on variant and polymorphic value theme.
+ *
+ * A tag based dispatch
+ *
+ * Supported operations:
+ * - construction
+ * - destruction
+ * - clone a = b;
+ * - cast a.cast<T>()
+ * - multi-method cast to common base a.cast<B>()
+ * - multi-method visit
+ */
+template <typename... Ts>
+class PolyValue : private ControlBlockVTable<Ts, Ts...>... {
+ static_assert(detail::is_unique_v<Ts...>, "Types must be unique");
+ static_assert(std::conjunction_v<std::is_empty<ControlBlockVTable<Ts, Ts...>>...>,
+ "VTable base classes must be empty");
+
+ ControlBlock<Ts...>* _object{nullptr};
+
+ PolyValue(ControlBlock<Ts...>* object) noexcept : _object(object) {}
+
+ auto tag() const noexcept {
+ return _object->getRuntimeTag();
+ }
+
+ void check() const {
+ if (!_object) {
+ throw std::logic_error("PolyValue is empty");
+ }
+ }
+
+ static void destroy(ControlBlock<Ts...>* object) {
+ static constexpr std::array destroyTbl = {&ControlBlockVTable<Ts, Ts...>::destroy...};
+
+ destroyTbl[object->getRuntimeTag()](object);
+ }
+
+public:
+ PolyValue() = delete;
+
+ PolyValue(const PolyValue& other) {
+ static constexpr std::array cloneTbl = {&ControlBlockVTable<Ts, Ts...>::clone...};
+ if (other._object) {
+ _object = cloneTbl[other.tag()](other._object);
+ }
+ }
+
+ PolyValue(PolyValue&& other) noexcept {
+ swap(other);
+ }
+
+ ~PolyValue() noexcept {
+ if (_object) {
+ destroy(_object);
+ }
+ }
+
+ PolyValue& operator=(PolyValue other) noexcept {
+ swap(other);
+ return *this;
+ }
+
+ template <typename T, typename... Args>
+ static PolyValue make(Args&&... args) {
+ return PolyValue{ControlBlockVTable<T, Ts...>::make(std::forward<Args>(args)...)};
+ }
+
+ template <int I>
+ using get_t = detail::get_type_by_index<I, Ts...>;
+
+ template <typename V, typename... Args>
+ auto visit(V&& v, Args&&... args) {
+ // unfortunately gcc rejects much nicer code, clang and msvc accept
+ // static constexpr std::array visitTbl = { &ControlBlockVTable<Ts, Ts...>::template
+ // visit<V>... };
+
+ using FunPtrType =
+ decltype(&ControlBlockVTable<get_t<0>, Ts...>::template visit<V, Args...>);
+ static constexpr FunPtrType visitTbl[] = {
+ &ControlBlockVTable<Ts, Ts...>::template visit<V, Args...>...};
+
+ check();
+ return visitTbl[tag()](std::forward<V>(v), *this, _object, std::forward<Args>(args)...);
+ }
+
+ template <typename V, typename... Args>
+ auto visit(V&& v, Args&&... args) const {
+ // unfortunately gcc rejects much nicer code, clang and msvc accept
+ // static constexpr std::array visitTbl = { &ControlBlockVTable<Ts, Ts...>::template
+ // visitConst<V>... };
+
+ using FunPtrType =
+ decltype(&ControlBlockVTable<get_t<0>, Ts...>::template visitConst<V, Args...>);
+ static constexpr FunPtrType visitTbl[] = {
+ &ControlBlockVTable<Ts, Ts...>::template visitConst<V, Args...>...};
+
+ check();
+ return visitTbl[tag()](std::forward<V>(v), *this, _object, std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ T* cast() {
+ check();
+ static constexpr std::array castTbl = {&ControlBlockVTable<Ts, Ts...>::template cast<T>...};
+ return castTbl[tag()](_object);
+ }
+
+ template <typename T>
+ const T* cast() const {
+ static constexpr std::array castTbl = {
+ &ControlBlockVTable<Ts, Ts...>::template castConst<T>...};
+
+ check();
+ return castTbl[tag()](_object);
+ }
+
+ template <typename T>
+ bool is() const {
+ static constexpr std::array isTbl = {ControlBlockVTable<Ts, Ts...>::template is_v<T>...};
+
+ check();
+ return isTbl[tag()];
+ }
+
+ bool empty() const {
+ return !_object;
+ }
+
+ void swap(PolyValue& other) noexcept {
+ std::swap(other._object, _object);
+ }
+
+ bool operator==(const PolyValue& rhs) const noexcept {
+ static constexpr std::array cmp = {ControlBlockVTable<Ts, Ts...>::compareEq...};
+ return cmp[tag()](_object, rhs._object);
+ }
+};
+
+} // namespace algebra
+} // namespace mongo::optimizer