// itlib-any v1.00 // // An alternative implementation of C++17's std::any // // SPDX-License-Identifier: MIT // MIT License: // Copyright(c) 2023 Borislav Stanimirov // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files(the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and / or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions : // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // // VERSION HISTORY // // 1.00 (2023-02-14) Initial release // // // DOCUMENTATION // // Simply include this file wherever you need. // It defines the type itlib::any, which is a reimplementation of C++17's // std::any with the following differences: // * A custom allocator can be provided to itlib::any. The allocator is // compatible with C++20's std::pmr::polymorphic_allocator and // itlib::pmr_allocator. A default allocator is provided. // * The type in itlib::any does not need to be copyable. If a copy is // attempted for a non-copyable type, std::bad_cast is thrown // // TESTS // // You can find unit tests for qalgorithm in its official repo: // https://github.com/iboB/itlib/blob/master/test/ // #include #include #include #include #include #include namespace itlib { template class any; namespace anyimpl { struct default_allocator { void* allocate_bytes(std::size_t n, std::size_t a) { void* ret = #if defined(_MSC_VER) _aligned_malloc(n, a); #else aligned_alloc(a, n); #endif if (!ret) throw std::bad_alloc{}; return ret; } void deallocate_bytes(void* p, std::size_t /*n*/, std::size_t /*a*/) noexcept { #if defined(_MSC_VER) _aligned_free(p); #else free(p); #endif } }; struct obj_block { uint32_t size; // size of block uint32_t alignment; // alignment of block virtual ~obj_block() = default; virtual void* data() noexcept = 0; virtual void clone_to(obj_block* buf) const = 0; }; template struct obj_block_for final : public obj_block { T m_data; template obj_block_for(Args&&... args) : m_data(std::forward(args)...) {} ~obj_block_for() = default; void* data() noexcept override { return &m_data; } template ::value, int>::type = 0> [[noreturn]] void do_clone_to(obj_block*) const { throw std::bad_cast(); } template ::value, int>::type = 0> void do_clone_to(obj_block* buf) const { new (buf) obj_block_for(m_data); } virtual void clone_to(obj_block* buf) const override { do_clone_to<>(buf); } }; // copied from itlib-type_traits template struct is_any : public std::false_type {}; template struct is_any> : public std::true_type {}; } template class any : private /*EBO*/ Alloc { anyimpl::obj_block* m_block = nullptr; public: using allocator_type = Alloc; any() noexcept = default; any(const Alloc& a) noexcept : Alloc(a) {} any(any&& o) noexcept : Alloc(o), m_block(o.m_block) { o.m_block = nullptr; } any& operator=(any&& o) noexcept { if (&o == this) return *this; // prevent self usurp reset(); m_block = o.m_block; o.m_block = nullptr; return *this; } template any(const any& o, const Alloc& a = {}) : Alloc(a) { copy_from(o); } any(const any& o) : any(o, Alloc{}) {} any& operator=(const any& o) { copy_from(o); return *this; } // only enable these if T is not another any template ::type>::value, int>::type = 0> any(T&& t, const Alloc& a = {}) : Alloc(a) { emplace(std::forward(t)); } template ::type>::value, int>::type = 0> any& operator=(T&& t) { emplace(std::forward(t)); return *this; } ~any() { reset(); } bool has_value() const noexcept { return !!m_block; } explicit operator bool() const noexcept { return has_value(); } void* data() noexcept { if (m_block) return m_block->data(); return nullptr; } const void* data() const noexcept { if (m_block) return m_block->data(); return nullptr; } template T* tdata() noexcept { return static_cast(data()); } template const T* tdata() const noexcept { return static_cast(data()); } Alloc get_allocator() const noexcept { return *this; } void reset() noexcept { if (!m_block) return; auto size = m_block->size; auto alignment = m_block->alignment; m_block->~obj_block(); free_block(size, alignment); } template T& emplace(Args&&... args) { reset(); using obj_block_for_t = anyimpl::obj_block_for; constexpr uint32_t size = sizeof(obj_block_for_t); constexpr uint32_t alignment = alignof(obj_block_for_t); m_block = static_cast(Alloc::allocate_bytes(size, alignment)); try { auto r = new (m_block) obj_block_for_t(std::forward(args)...); r->size = size; r->alignment = alignment; return r->m_data; } catch (...) { free_block(size, alignment); throw; } } template void copy_from(const any& o) { reset(); if (!o.has_value()) return; m_block = static_cast(Alloc::allocate_bytes(o.m_block->size, o.m_block->alignment)); try { o.m_block->clone_to(m_block); m_block->size = o.m_block->size; m_block->alignment = o.m_block->alignment; } catch (...) { free_block(o.m_block->size, o.m_block->alignment); throw; } } private: void free_block(size_t size, size_t alignment) noexcept { Alloc::deallocate_bytes(m_block, size, alignment); m_block = nullptr; } }; }