#include #include #include "../common/compat.hpp" #if (defined(_WIN32) && _MSC_VER < 1900 && !defined(alignof)) # define alignof __alignof # define SPRAWL_DEFINED_ALIGNOF #endif namespace sprawl { namespace memory { // 8 is too large for some types, but no type should be larger than 8 // and 8 is a multiple of other valid aligments (1,2,4) so it's a safe default template class OpaqueType; template struct CreateAs{}; namespace detail { template struct ComputeSize { static size_t const size = accumulatedSoFar + ((accumulatedSoFar % align != 0) ? (align - (accumulatedSoFar % align)) : 0); }; //alignof(double) is broken in 32 bit gcc and clang, and returns 8 when it should return 4. #if defined(__GNUC__) && SPRAWL_32_BIT template struct ComputeSize { static size_t const size = ComputeSize< accumulatedSoFar + sizeof(double) + (accumulatedSoFar % 4 != 0 ? (4 - (accumulatedSoFar % 4)) : 0), align, MoreTypes... >::size; }; #endif template struct ComputeSize { static size_t const size = ComputeSize< accumulatedSoFar + sizeof(FirstType) + (accumulatedSoFar % alignof(FirstType) != 0 ? (alignof(FirstType) - (accumulatedSoFar % alignof(FirstType))) : 0), align, MoreTypes... >::size; }; template struct ComputeAlign { static size_t const align = highestSoFar; }; #if defined(__GNUC__) && SPRAWL_32_BIT template struct ComputeAlign { static size_t const align = ComputeAlign< (highestSoFar < 4 ? 4 : highestSoFar), MoreTypes... >::align; }; #endif template struct ComputeAlign { static size_t const align = ComputeAlign< (highestSoFar < alignof(FirstType) ? alignof(FirstType) : highestSoFar), MoreTypes... >::align; }; } template using OpaqueTypeList = OpaqueType< detail::ComputeSize<0, detail::ComputeAlign<0, Types...>::align, Types...>::size, detail::ComputeAlign<0, Types...>::align >; } } template class sprawl::memory::OpaqueType { public: class Deleter { public: virtual void Delete(void*) = 0; }; template class TypeDeleter : public Deleter { public: virtual void Delete(void* ptr) { reinterpret_cast(ptr)->~T(); } }; template OpaqueType(CreateAs const&, Params&&... params) : m_ptr() , m_deleter() { static_assert(sizeof(T) == size, "Opaque type delcared with size that does not match the type used to construct it."); static_assert(align >= alignof(T) && (align % alignof(T) == 0), "Opaque type definition does not match alignment requirements of the type used to construct it."); new(&m_ptr) T(std::forward(params)...); new(&m_deleter) TypeDeleter(); } ~OpaqueType() { reinterpret_cast(&m_deleter)->Delete(&m_ptr); } template T& As() { static_assert(sizeof(T) == size, "Opaque type delcared with size that does not match the type used to access it."); static_assert(align >= alignof(T) && (align % alignof(T) == 0), "Opaque type definition does not match alignment requirements of the type used to access it."); return *reinterpret_cast(&m_ptr); } template T const& As() const { static_assert(sizeof(T) == size, "Opaque type delcared with size that does not match the type used to access it."); static_assert(align >= alignof(T) && (align % alignof(T) == 0), "Opaque type definition does not match alignment requirements of the type used to access it."); return *reinterpret_cast(&m_ptr); } template operator T&() { static_assert(sizeof(T) == size, "Opaque type delcared with size that does not match the type used to access it."); static_assert(align >= alignof(T) && (align % alignof(T) == 0), "Opaque type definition does not match alignment requirements of the type used to access it."); return *reinterpret_cast(&m_ptr); } template operator T const&() const { static_assert(sizeof(T) == size, "Opaque type delcared with size that does not match the type used to access it."); static_assert(align >= alignof(T) && (align % alignof(T) == 0), "Opaque type definition does not match alignment requirements of the type used to access it."); return *reinterpret_cast(&m_ptr); } private: typename std::aligned_storage::type m_ptr; typename std::aligned_storage::type m_deleter; }; #ifdef SPRAWL_DEFINED_ALIGNOF # undef alignof # undef SPRAWL_DEFINED_ALIGNOF #endif