about summary refs log tree commit diff
path: root/immer/detail/combine_standard_layout.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'immer/detail/combine_standard_layout.hpp')
-rw-r--r--immer/detail/combine_standard_layout.hpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/immer/detail/combine_standard_layout.hpp b/immer/detail/combine_standard_layout.hpp
new file mode 100644
index 000000000000..55c69bc91682
--- /dev/null
+++ b/immer/detail/combine_standard_layout.hpp
@@ -0,0 +1,235 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <type_traits>
+
+#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ == 1
+#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 1
+#define immer_offsetof(st, m) ((std::size_t) & (((st*) 0)->m))
+#else
+#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 0
+#define immer_offsetof offsetof
+#endif
+
+namespace immer {
+namespace detail {
+
+//
+// Metafunction that returns a standard layout struct that combines
+// all the standard layout types in `Ts...`, while making sure that
+// empty base optimizations are used.
+//
+// To query a part of the type do `get<my_part>(x)`;
+//
+// This is useful when putting together a type that merges various
+// types coming from different policies.  Some of them might be empty,
+// so we shall enable empty base optimizations.  But if we just
+// inherit from all of them, we would break the "standard layout"
+// rules, preventing us from using `offseof(...)`.  So metafunction
+// will generate the type by sometimes inheriting, sometimes adding as
+// member.
+//
+// Note that the types are added to the combined type from right to
+// left!
+//
+template <typename... Ts>
+struct combine_standard_layout;
+
+template <typename... Ts>
+using combine_standard_layout_t = typename combine_standard_layout<Ts...>::type;
+
+namespace csl {
+
+template <typename T>
+struct type_t
+{};
+
+template <typename U, typename T>
+U& get(T& x);
+
+template <typename U, typename T>
+const U& get(const T& x);
+
+template <typename T, typename Next = void>
+struct inherit
+{
+    struct type
+        : T
+        , Next
+    {
+        using Next::get_;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return *this; }
+        const T& get_(type_t<T>) const { return *this; }
+    };
+};
+
+template <typename T>
+struct inherit<T, void>
+{
+    struct type : T
+    {
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return *this; }
+        const T& get_(type_t<T>) const { return *this; }
+    };
+};
+
+template <typename T, typename Next = void>
+struct member
+{
+    struct type : Next
+    {
+        T d;
+
+        using Next::get_;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return d; }
+        const T& get_(type_t<T>) const { return d; }
+    };
+};
+
+template <typename T>
+struct member<T, void>
+{
+    struct type
+    {
+        T d;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return d; }
+        const T& get_(type_t<T>) const { return d; }
+    };
+};
+
+template <typename T, typename Next>
+struct member_two
+{
+    struct type
+    {
+        Next n;
+        T d;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return d; }
+        const T& get_(type_t<T>) const { return d; }
+
+        template <typename U>
+        auto get_(type_t<U> t) -> decltype(auto)
+        {
+            return n.get_(t);
+        }
+        template <typename U>
+        auto get_(type_t<U> t) const -> decltype(auto)
+        {
+            return n.get_(t);
+        }
+    };
+};
+
+template <typename... Ts>
+struct combine_standard_layout_aux;
+
+template <typename T>
+struct combine_standard_layout_aux<T>
+{
+    static_assert(std::is_standard_layout<T>::value, "");
+
+    using type = typename std::conditional_t<std::is_empty<T>::value,
+                                             csl::inherit<T>,
+                                             csl::member<T>>::type;
+};
+
+template <typename T, typename... Ts>
+struct combine_standard_layout_aux<T, Ts...>
+{
+    static_assert(std::is_standard_layout<T>::value, "");
+
+    using this_t = T;
+    using next_t = typename combine_standard_layout_aux<Ts...>::type;
+
+    static constexpr auto empty_this = std::is_empty<this_t>::value;
+    static constexpr auto empty_next = std::is_empty<next_t>::value;
+
+    using type = typename std::conditional_t<
+        empty_this,
+        inherit<this_t, next_t>,
+        std::conditional_t<empty_next,
+                           member<this_t, next_t>,
+                           member_two<this_t, next_t>>>::type;
+};
+
+} // namespace csl
+
+using csl::get;
+
+template <typename... Ts>
+struct combine_standard_layout
+{
+    using type = typename csl::combine_standard_layout_aux<Ts...>::type;
+#if !IMMER_BROKEN_STANDARD_LAYOUT_DETECTION
+    static_assert(std::is_standard_layout<type>::value, "");
+#endif
+};
+
+} // namespace detail
+} // namespace immer