|
#pragma once |
|
|
|
#include <cstddef> |
|
#include <cassert> |
|
#include <new> |
|
#include <typeinfo> |
|
#include <utility> |
|
#include <initializer_list> |
|
#include <type_traits> |
|
#include <exception> |
|
#include "tagged_ptr.hpp" |
|
|
|
class bad_any_cast : public std::bad_cast |
|
{ |
|
public: |
|
const char* what() const noexcept override |
|
{ |
|
return message; |
|
} |
|
private: |
|
inline static constexpr char message[] = "bad any_cast"; |
|
}; |
|
|
|
class any |
|
{ |
|
private: |
|
alignas(std::max_align_t) std::byte m_buffer[sizeof(std::max_align_t)]; |
|
tagged_ptr<const std::type_info> m_ptypeinfo; |
|
|
|
enum class func_op_t { copy, soo_copy, soo_move, soo_destruct, destroy }; |
|
|
|
void (*m_func_table)(func_op_t, void*, std::byte*); |
|
|
|
template <typename T> |
|
inline static void m_func_table_init(func_op_t func_op, void* pobj, std::byte* addr) |
|
{ |
|
switch (func_op) |
|
{ |
|
case func_op_t::copy: |
|
new(addr) T*(new T(**std::launder(reinterpret_cast<T**>(pobj)))); |
|
break; |
|
case func_op_t::soo_copy: |
|
new(addr) T(*std::launder(reinterpret_cast<T*>(pobj))); |
|
break; |
|
case func_op_t::soo_move: |
|
new(addr) T(std::move(*std::launder(reinterpret_cast<T*>(pobj)))); |
|
break; |
|
case func_op_t::soo_destruct: |
|
std::launder(reinterpret_cast<T*>(pobj))->~T(); |
|
break; |
|
case func_op_t::destroy: |
|
delete *std::launder(reinterpret_cast<T**>(pobj)); |
|
break; |
|
default: |
|
assert(0 && "Invalid func_op."); |
|
} |
|
} |
|
|
|
void copy(const void* pobj, std::byte* addr) const |
|
{ |
|
m_func_table(func_op_t::copy, const_cast<void*>(pobj), addr); |
|
} |
|
|
|
void soo_copy(const void* pobj, std::byte* addr) const |
|
{ |
|
m_func_table(func_op_t::soo_copy, const_cast<void*>(pobj), addr); |
|
} |
|
|
|
void soo_move(void* pobj, std::byte* addr) noexcept |
|
{ |
|
m_func_table(func_op_t::soo_move, pobj, addr); |
|
} |
|
|
|
void soo_destruct(void* pobj) noexcept |
|
{ |
|
m_func_table(func_op_t::soo_destruct, pobj, nullptr); |
|
} |
|
|
|
void destroy(void* pobj) noexcept |
|
{ |
|
m_func_table(func_op_t::destroy, pobj, nullptr); |
|
} |
|
|
|
template <typename T> |
|
struct uses_soo : std::bool_constant<std::is_nothrow_move_constructible_v<T> && (alignof(T) <= alignof(decltype(m_buffer))) && (sizeof(T) <= sizeof(m_buffer))> {}; |
|
|
|
template <typename T> |
|
inline static constexpr bool uses_soo_v = uses_soo<T>::value; |
|
|
|
template <typename T, typename ...Args> |
|
std::decay_t<T>& construct(Args&&... args) |
|
{ |
|
std::decay_t<T>* ret; |
|
if constexpr (uses_soo_v<std::decay_t<T>>) |
|
{ |
|
ret = new(m_buffer) std::decay_t<T>(std::forward<Args>(args)...); |
|
} |
|
else |
|
{ |
|
ret = *(new(m_buffer) std::decay_t<T>*(new std::decay_t<T>(std::forward<Args>(args)...))); |
|
m_ptypeinfo.set_tag(1); |
|
} |
|
m_ptypeinfo = &typeid(std::decay_t<T>); |
|
m_func_table = m_func_table_init<std::decay_t<T>>; |
|
return *ret; |
|
} |
|
|
|
public: |
|
constexpr any() noexcept : m_ptypeinfo(nullptr) {} |
|
|
|
any(const any& other) : m_ptypeinfo(other.m_ptypeinfo) |
|
{ |
|
if (m_ptypeinfo) |
|
{ |
|
if (m_ptypeinfo.has_tag()) |
|
{ |
|
other.copy(other.m_buffer, m_buffer); |
|
} |
|
else |
|
{ |
|
other.soo_copy(other.m_buffer, m_buffer); |
|
} |
|
m_func_table = other.m_func_table; |
|
} |
|
} |
|
|
|
any(any&& other) noexcept : any() |
|
{ |
|
swap(other); |
|
} |
|
|
|
template <typename T> |
|
requires std::conjunction_v<std::negation<std::is_same<std::decay_t<T>, any>>, std::negation<std::is_same<std::decay_t<T>, std::in_place_type_t<T>>>, std::is_copy_constructible<std::decay_t<T>>> |
|
any(T&& t) |
|
{ |
|
construct<T>(std::forward<T>(t)); |
|
} |
|
|
|
template <typename T, typename ...Args> |
|
requires std::conjunction_v<std::is_constructible<std::decay_t<T>, Args...>, std::is_copy_constructible<std::decay_t<T>>> |
|
explicit any(std::in_place_type_t<T>, Args&&... args) |
|
{ |
|
construct<T>(std::forward<Args>(args)...); |
|
} |
|
|
|
template <typename T, typename U, typename ...Args> |
|
requires std::conjunction_v<std::is_constructible<std::decay_t<T>, std::initializer_list<U>&, Args...>, std::is_copy_constructible<std::decay_t<T>>> |
|
explicit any(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args) |
|
{ |
|
construct<T>(il, std::forward<Args>(args)...); |
|
} |
|
|
|
~any() |
|
{ |
|
reset(); |
|
} |
|
|
|
any& operator=(const any& other) |
|
{ |
|
if (this != &other) |
|
{ |
|
any(other).swap(*this); |
|
} |
|
return *this; |
|
} |
|
|
|
any& operator=(any&& other) noexcept |
|
{ |
|
if (this != &other) |
|
{ |
|
swap(other); |
|
} |
|
return *this; |
|
} |
|
|
|
template <typename T> |
|
requires std::conjunction_v<std::negation<std::is_same<std::decay_t<T>, any>>, std::is_copy_constructible<std::decay_t<T>>> |
|
any& operator=(T&& t) |
|
{ |
|
any(std::forward<T>(t)).swap(*this); |
|
return *this; |
|
} |
|
|
|
template <typename T, typename ...Args> |
|
requires std::conjunction_v<std::is_constructible<std::decay_t<T>, Args...>, std::is_copy_constructible<std::decay_t<T>>> |
|
std::decay_t<T>& emplace(Args&&... args) |
|
{ |
|
reset(); |
|
return construct<T>(std::forward<Args>(args)...); |
|
} |
|
|
|
template <typename T, typename U, typename ...Args> |
|
requires std::conjunction_v<std::is_constructible<std::decay_t<T>, std::initializer_list<U>&, Args...>, std::is_copy_constructible<std::decay_t<T>>> |
|
std::decay_t<T>& emplace(std::initializer_list<U> il, Args&&... args) |
|
{ |
|
reset(); |
|
return construct<T>(il, std::forward<Args>(args)...); |
|
} |
|
|
|
void reset() noexcept |
|
{ |
|
if (m_ptypeinfo) |
|
{ |
|
if (m_ptypeinfo.has_tag()) |
|
{ |
|
destroy(m_buffer); |
|
} |
|
else |
|
{ |
|
soo_destruct(m_buffer); |
|
} |
|
m_ptypeinfo.clear(); |
|
} |
|
} |
|
|
|
void swap(any& other) noexcept |
|
{ |
|
if (m_ptypeinfo && other.m_ptypeinfo) |
|
{ |
|
alignas(decltype(m_buffer)) std::byte tempbuffer[sizeof(m_buffer)]; |
|
if (m_ptypeinfo.has_tag() && other.m_ptypeinfo.has_tag()) |
|
{ |
|
std::swap(m_buffer, other.m_buffer); |
|
} |
|
else if (m_ptypeinfo.has_tag()) |
|
{ |
|
other.soo_move(other.m_buffer, tempbuffer); |
|
other.soo_destruct(other.m_buffer); |
|
std::swap(m_buffer, other.m_buffer); |
|
other.soo_move(tempbuffer, m_buffer); |
|
other.soo_destruct(tempbuffer); |
|
} |
|
else if (other.m_ptypeinfo.has_tag()) |
|
{ |
|
soo_move(m_buffer, tempbuffer); |
|
soo_destruct(m_buffer); |
|
std::swap(m_buffer, other.m_buffer); |
|
soo_move(tempbuffer, other.m_buffer); |
|
soo_destruct(tempbuffer); |
|
} |
|
else |
|
{ |
|
soo_move(m_buffer, tempbuffer); |
|
soo_destruct(m_buffer); |
|
other.soo_move(other.m_buffer, m_buffer); |
|
other.soo_destruct(other.m_buffer); |
|
soo_move(tempbuffer, other.m_buffer); |
|
soo_destruct(tempbuffer); |
|
} |
|
::swap(m_ptypeinfo, other.m_ptypeinfo); |
|
std::swap(m_func_table, other.m_func_table); |
|
} |
|
else if (m_ptypeinfo) |
|
{ |
|
if (m_ptypeinfo.has_tag()) |
|
{ |
|
std::swap(m_buffer, other.m_buffer); |
|
} |
|
else |
|
{ |
|
soo_move(m_buffer, other.m_buffer); |
|
soo_destruct(m_buffer); |
|
} |
|
::swap(m_ptypeinfo, other.m_ptypeinfo); |
|
std::swap(m_func_table, other.m_func_table); |
|
} |
|
else if (other.m_ptypeinfo) |
|
{ |
|
if (other.m_ptypeinfo.has_tag()) |
|
{ |
|
std::swap(m_buffer, other.m_buffer); |
|
} |
|
else |
|
{ |
|
other.soo_move(other.m_buffer, m_buffer); |
|
other.soo_destruct(other.m_buffer); |
|
} |
|
::swap(m_ptypeinfo, other.m_ptypeinfo); |
|
std::swap(m_func_table, other.m_func_table); |
|
} |
|
else |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
bool has_value() const noexcept |
|
{ |
|
return m_ptypeinfo != nullptr; |
|
} |
|
|
|
const std::type_info& type() const noexcept |
|
{ |
|
return m_ptypeinfo ? *m_ptypeinfo : typeid(void); |
|
} |
|
|
|
template <typename T> |
|
friend T any_cast(const any&); |
|
|
|
template <typename T> |
|
friend T any_cast(any&); |
|
|
|
template <typename T> |
|
friend T any_cast(any&&); |
|
|
|
template <typename T> |
|
friend const T* any_cast(const any*) noexcept; |
|
|
|
template <typename T> |
|
friend T* any_cast(any*) noexcept; |
|
|
|
friend void swap(any&, any&) noexcept; |
|
}; |
|
|
|
template <typename T> |
|
const T* any_cast(const any* op) noexcept |
|
{ |
|
static_assert(!std::is_void_v<T>, "T cannot be void."); |
|
if (op) |
|
{ |
|
if (typeid(T) == op->type()) |
|
{ |
|
if (op->m_ptypeinfo.has_tag()) |
|
{ |
|
return *std::launder(reinterpret_cast<const T**>(op->m_buffer)); |
|
} |
|
else |
|
{ |
|
return std::launder(reinterpret_cast<const T*>(op->m_buffer)); |
|
} |
|
} |
|
else |
|
{ |
|
return nullptr; |
|
} |
|
} |
|
else |
|
{ |
|
return nullptr; |
|
} |
|
} |
|
|
|
template <typename T> |
|
T* any_cast(any* op) noexcept |
|
{ |
|
static_assert(!std::is_void_v<T>, "T cannot be void."); |
|
if (op) |
|
{ |
|
if (typeid(T) == op->type()) |
|
{ |
|
if (op->m_ptypeinfo.has_tag()) |
|
{ |
|
return *std::launder(reinterpret_cast<T**>(op->m_buffer)); |
|
} |
|
else |
|
{ |
|
return std::launder(reinterpret_cast<T*>(op->m_buffer)); |
|
} |
|
} |
|
else |
|
{ |
|
return nullptr; |
|
} |
|
} |
|
else |
|
{ |
|
return nullptr; |
|
} |
|
} |
|
|
|
template <typename T> |
|
T any_cast(const any& op) |
|
{ |
|
if (auto r = any_cast<std::remove_cvref_t<T>>(&op); r) |
|
{ |
|
return static_cast<T>(*r); |
|
} |
|
else |
|
{ |
|
throw bad_any_cast{}; |
|
} |
|
} |
|
|
|
template <typename T> |
|
T any_cast(any& op) |
|
{ |
|
if (auto r = any_cast<std::remove_cvref_t<T>>(&op); r) |
|
{ |
|
return static_cast<T>(*r); |
|
} |
|
else |
|
{ |
|
throw bad_any_cast{}; |
|
} |
|
} |
|
|
|
template <typename T> |
|
T any_cast(any&& op) |
|
{ |
|
if (auto r = any_cast<std::remove_cvref_t<T>>(&op); r) |
|
{ |
|
return static_cast<T>(std::move(*r)); |
|
} |
|
else |
|
{ |
|
throw bad_any_cast{}; |
|
} |
|
} |
|
|
|
void swap(any& lhs, any& rhs) noexcept |
|
{ |
|
lhs.swap(rhs); |
|
} |
|
|
|
template <typename T, typename ...Args> |
|
any make_any(Args&&... args) |
|
{ |
|
return any(std::in_place_type<T>, std::forward<Args>(args)...); |
|
} |
|
|
|
template <typename T, typename U, typename ...Args> |
|
any make_any(std::initializer_list<U> il, Args&&... args) |
|
{ |
|
return any(std::in_place_type<T>, il, std::forward<Args>(args)...); |
|
} |