diff options
Diffstat (limited to 'immer/detail/combine_standard_layout.hpp')
-rw-r--r-- | immer/detail/combine_standard_layout.hpp | 235 |
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 |