git-subtree-dir: third_party/immer git-subtree-split: ad3e3556d38bb75966dd24c61a774970a7c7957e
235 lines
5.6 KiB
C++
235 lines
5.6 KiB
C++
//
|
|
// 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
|