Created
April 5, 2018 18:04
-
-
Save kris-jusiak/a3a325b692c4188395f41405dffa38c8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Copyright (c) 2018 Kris Jusiak (kris at jusiak dot net) | |
// | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
// | |
#pragma once | |
#if not defined(__cpp_variadic_templates) or \ | |
not defined(__cpp_rvalue_references) or not defined(__cpp_decltype) or \ | |
not defined(__cpp_alias_templates) or \ | |
not defined(__cpp_generic_lambdas) or not defined(__cpp_constexpr) or \ | |
not defined(__cpp_return_type_deduction) or \ | |
not defined(__cpp_fold_expressions) or not defined(__cpp_static_assert) or \ | |
not defined(__cpp_delegating_constructors) | |
#error "Type.Erasure requires C++17 support" | |
#else | |
#pragma GCC system_header | |
#include <type_traits> | |
#include <utility> | |
namespace te { | |
inline namespace v1 { | |
namespace detail { | |
template <class...> | |
struct type_list {}; | |
template <class, std::size_t> | |
struct mappings final { | |
friend auto get(mappings); | |
template <class T> | |
struct set { | |
friend auto get(mappings) { return T{}; } | |
}; | |
}; | |
template <std::size_t, class...> | |
constexpr std::size_t mappings_size_impl(...) { | |
return {}; | |
} | |
template <std::size_t N, class T, class... Ts> | |
constexpr auto mappings_size_impl(bool dummy) | |
-> decltype(get(mappings<T, N>{}), std::size_t{}) { | |
return 1 + mappings_size_impl<N + 1, T, Ts...>(dummy); | |
} | |
template <class... Ts> | |
constexpr auto mappings_size() { | |
return mappings_size_impl<1, Ts...>(bool{}); | |
} | |
template <class T, class = decltype(sizeof(T))> | |
std::true_type is_complete_impl(bool); | |
template <class> | |
std::false_type is_complete_impl(...); | |
template <class T> | |
struct is_complete : decltype(is_complete_impl<T>(bool{})) {}; | |
template <class T> | |
constexpr auto requires__(bool) | |
-> decltype(std::declval<T>().template requires__<T>()); | |
template <class> | |
constexpr auto requires__(...) -> void; | |
template <class TExpr> | |
class expr_wrapper final { | |
static_assert(std::is_empty<TExpr>{}); | |
public: | |
template <class... Ts> | |
decltype(auto) operator()(Ts &&... args) const { | |
return reinterpret_cast<const TExpr &>(*this)(std::forward<Ts>(args)...); | |
} | |
}; | |
class void_ptr final { | |
public: | |
using ptr_t = void *; | |
using del_t = void (*)(ptr_t); | |
using copy_t = ptr_t (*)(ptr_t); | |
public: | |
void_ptr() noexcept = default; | |
template <class T> | |
constexpr explicit void_ptr( | |
T *ptr = nullptr, | |
del_t del = [](ptr_t ptr) { delete static_cast<T *>(ptr); }, | |
copy_t copy = [](ptr_t ptr) -> void * { | |
return new T{*static_cast<T *>(ptr)}; | |
}) noexcept | |
: ptr{ptr}, del{del}, copy{copy} {} | |
constexpr void_ptr(const void_ptr &other) noexcept | |
: ptr{other.copy(other.ptr)}, del{other.del}, copy{other.copy} {} | |
constexpr void_ptr(void_ptr &&other) noexcept | |
: ptr{std::move(other.ptr)}, | |
del{std::move(other.del)}, | |
copy{std::move(other.copy)} { | |
other.ptr = nullptr; | |
} | |
constexpr void_ptr &operator=(const void_ptr &other) noexcept { | |
reset(other.copy(other.ptr)); | |
del = other.del; | |
copy = other.copy; | |
return *this; | |
} | |
constexpr void_ptr &operator=(void_ptr &&other) noexcept { | |
reset(std::move(other.ptr)); | |
del = std::move(other.del); | |
copy = std::move(other.copy); | |
other.ptr = nullptr; | |
return *this; | |
} | |
~void_ptr() noexcept { reset(); } | |
constexpr void reset(ptr_t new_ptr = {}) noexcept { | |
del(ptr); | |
ptr = new_ptr; | |
} | |
constexpr void reset(ptr_t new_ptr, del_t new_del, copy_t new_copy) noexcept { | |
ptr = new_ptr; | |
del = new_del; | |
copy = new_copy; | |
} | |
template <class T = void> | |
constexpr decltype(auto) get() const noexcept { | |
return reinterpret_cast<T *>(ptr); | |
} | |
private: | |
ptr_t ptr; | |
del_t del; | |
copy_t copy; | |
}; | |
} // namespace detail | |
class dynamic_storage { | |
public: | |
template <class T, class T_ = std::decay_t<T> > | |
constexpr explicit dynamic_storage(T &&t, detail::void_ptr &ptr) noexcept { | |
ptr.reset( | |
new T_{std::forward<T>(t)}, | |
[](void *ptr) { delete static_cast<T_ *>(ptr); }, | |
[](void *ptr) -> void * { return new T_{*static_cast<T_ *>(ptr)}; }); | |
} | |
}; | |
template <std::size_t Size, std::size_t Alignment = 16> | |
class local_storage { | |
public: | |
template <class T, class T_ = std::decay_t<T> > | |
constexpr explicit local_storage(T &&t, detail::void_ptr &ptr) noexcept { | |
static_assert(sizeof(T) <= Size); | |
new (&data) T_{std::forward<T>(t)}; | |
ptr.reset(&data, [](void *ptr) { static_cast<T_ *>(ptr)->~T_(); }, | |
[](void *ptr) -> void * { | |
return new (ptr) T_{*static_cast<T_ *>(ptr)}; | |
}); | |
} | |
private: | |
std::aligned_storage<Size, Alignment> data; | |
}; | |
class static_vtable { | |
using ptr_t = void *; | |
public: | |
template <class T, std::size_t Size> | |
static_vtable(T &&, ptr_t *&vtable, | |
std::integral_constant<std::size_t, Size>) noexcept | |
: vt{new ptr_t[Size]} | |
{ | |
vtable = vt; | |
} | |
static_vtable(static_vtable&& other) noexcept | |
: vt{other.vt} | |
{ | |
other.vt = nullptr; | |
} | |
~static_vtable() { | |
delete[] vt; | |
} | |
ptr_t* vt{}; | |
}; | |
namespace detail { | |
struct poly_base { | |
detail::void_ptr ptr{}; | |
detail::void_ptr::ptr_t *vptr{}; | |
}; | |
} // namespace detail | |
template <class I, class TStorage = dynamic_storage, | |
class TVtable = static_vtable> | |
class poly : detail::poly_base, | |
public std::conditional_t<detail::is_complete<I>{}, I, | |
detail::type_list<I> > { | |
public: | |
template <class T, | |
class = std::enable_if_t<not std::is_convertible<T, poly>{} and | |
std::is_copy_constructible<T>{} and | |
std::is_destructible<T>{}> > | |
constexpr poly(T &&t) noexcept | |
: poly{std::forward<T>(t), | |
detail::type_list<decltype(detail::requires__<I>(bool{}))>{}} {} | |
constexpr poly(poly const &) noexcept = default; | |
constexpr poly &operator=(poly const &) noexcept = default; | |
constexpr poly(poly &&) noexcept = default; | |
constexpr poly &operator=(poly &&) noexcept = default; | |
private: | |
template <class T, class TRequires> | |
constexpr poly(T &&t, const TRequires) noexcept | |
: poly{std::forward<T>(t), | |
std::make_index_sequence<detail::mappings_size<I>()>{}} {} | |
template <class T, std::size_t... Ns> | |
constexpr poly(T &&t, std::index_sequence<Ns...>) noexcept | |
: detail::poly_base{}, | |
vtable{std::forward<T>(t), vptr, | |
std::integral_constant<std::size_t, sizeof...(Ns)>{}}, | |
storage{std::forward<T>(t), ptr} { | |
static_assert(sizeof...(Ns) > 0); | |
(init<Ns + 1, std::decay_t<T> >( | |
decltype(get(detail::mappings<I, Ns + 1>{})){}), | |
...); | |
} | |
template <std::size_t N, class T, class TExpr, class... TArgs> | |
constexpr void init(detail::type_list<TExpr, TArgs...>) noexcept { | |
vptr[N - 1] = reinterpret_cast<void *>(+[](void *self, TArgs... args) { | |
return detail::expr_wrapper<TExpr>{}(*static_cast<T *>(self), args...); | |
}); | |
} | |
TStorage storage; | |
TVtable vtable; | |
}; | |
namespace detail { | |
template <class I, std::size_t N, class R, class TExpr, class... Ts> | |
constexpr auto call_impl(const detail::poly_base &self, | |
std::integral_constant<std::size_t, N>, | |
detail::type_list<R>, const TExpr, | |
Ts &&... args) noexcept { | |
void(typename detail::mappings<I, N>::template set< | |
detail::type_list<TExpr, Ts...> >{}); | |
return reinterpret_cast<R (*)(void *, Ts...)>(self.vptr[N - 1])( | |
self.ptr.get(), std::forward<Ts>(args)...); | |
} | |
template <class I, class T, std::size_t... Ns> | |
constexpr auto extends_impl(std::index_sequence<Ns...>) noexcept { | |
(void(typename mappings<T, Ns + 1>::template set<decltype( | |
get(mappings<I, Ns + 1>{}))>{}), | |
...); | |
} | |
} // namespace detail | |
template <class R = void, std::size_t N = 0, class TExpr, class I, class... Ts> | |
constexpr auto call(const TExpr expr, const I &interface, | |
Ts &&... args) noexcept { | |
static_assert(std::is_empty<TExpr>{}); | |
return detail::call_impl<I>( | |
reinterpret_cast<const detail::poly_base &>(interface), | |
std::integral_constant<std::size_t, | |
detail::mappings_size<I, class call>() + 1>{}, | |
detail::type_list<R>{}, expr, std::forward<Ts>(args)...); | |
} | |
template <class I, class T> | |
constexpr auto extends(const T &) noexcept { | |
detail::extends_impl<I, T>( | |
std::make_index_sequence<detail::mappings_size<I, T>()>{}); | |
} | |
} // namespace v1 | |
} // namespace te | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment