#include #include #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; }; template struct ComputeSize { static size_t const size = ComputeSize< accumulatedSoFar + sizeof(FirstType) + (accumulatedSoFar % alignof(FirstType) != 0 ? (alignof(FirstType) - (accumulatedSoFar % alignof(FirstType))) : 0), MoreTypes... >::size; }; template struct ComputeAlign { static size_t const align = highestSoFar; }; template struct ComputeAlign { static size_t const align = ComputeAlign< (highestSoFar < alignof(FirstType) ? alignof(FirstType) : highestSoFar), MoreTypes... >::align; }; } template using OpaqueTypeList = OpaqueType< detail::ComputeSize<0, 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