/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2016-2018 Danny Robson */ #ifndef CRUFT_UTIL_ALLOC_RAW_DYNAMIC_HPP #define CRUFT_UTIL_ALLOC_RAW_DYNAMIC_HPP #include "traits.hpp" #include #include namespace util::alloc::raw { // wraps an allocator given at construction time, forwarding all calls to // the inner object. used to allow virtual dispatch of the non-virtual // allocator interface. class dynamic { public: struct alignment_unsupported : public std::exception {}; //--------------------------------------------------------------------- // disable copying, but allow moving (required for calls to 'make') dynamic (const dynamic&) = delete; dynamic& operator= (const dynamic&) = delete; dynamic (dynamic &&rhs) = default; dynamic& operator= (dynamic&&) = default; //--------------------------------------------------------------------- // construct an inner wrapper for type T. used to get around lack of // ambiguous template constructors. template static dynamic make (Args &&...args) { return dynamic ( std::make_unique> ( std::forward (args)... ) ); } //--------------------------------------------------------------------- // if aligned allocation is not exposed by the child then we will // unconditionally throw if it is ever called. unfortunately we can't // dynamically eliminate the function altogether given run-time // dynamic dispatch needs the common calls exposed to the clients, and // aligned allocate is stupid useful. template auto allocate (size_t bytes) { return m_child->allocate (bytes); } template auto allocate (size_t bytes, size_t alignment) { return m_child->allocate (bytes, alignment); } auto deallocate (void *ptr, size_t bytes) { return m_child->deallocate (ptr, bytes); } auto deallocate (void *ptr, size_t bytes, size_t alignment) { return m_child->deallocate (ptr, bytes, alignment); } //--------------------------------------------------------------------- auto begin (void) { return m_child->begin (); } auto begin (void) const { return m_child->begin (); } auto offset (const void *ptr) const { return m_child->offset (ptr); } //--------------------------------------------------------------------- auto reset (void) { return m_child->reset (); } //--------------------------------------------------------------------- // capacity queries auto capacity (void) const { return m_child->capacity (); } auto used (void) const { return m_child->used (); } auto remain (void) const { return m_child->remain (); } private: // Internal base for arbitrary allocator types. Necessary for // type ellision in super-client classes. class interface { public: interface () = default; interface (const interface&) = delete; interface (interface&&) = delete; interface& operator= (const interface&) = delete; interface& operator= (interface&&) = delete; virtual ~interface () { ; } // allocation management virtual util::view allocate (size_t bytes) = 0; virtual util::view allocate (size_t bytes, size_t alignment) = 0; virtual void deallocate (void *ptr, size_t bytes) = 0; virtual void deallocate (void *ptr, size_t bytes, size_t alignment) = 0; virtual std::byte* begin (void) = 0; virtual const std::byte* begin (void) const = 0; virtual std::byte* end (void) = 0; virtual const std::byte* end (void) const = 0; virtual size_t offset (const void*) const = 0; virtual void reset (void) = 0; // capacity queries virtual size_t capacity (void) const = 0; virtual size_t used (void) const = 0; virtual size_t remain (void) const = 0; }; template class child final : public interface { public: struct _alignment_unsupported : public alignment_unsupported { }; template child (Args &&...args): interface (), m_target (std::forward (args)...) { ; } // allocation management util::view allocate (size_t bytes) override { return m_target.allocate (bytes); } // we can't totally eliminate this call given the point is to // expose the common API area, but we will throw if the operation // is unsupported in the child. util::view allocate (size_t bytes, size_t alignment) override { if constexpr (has_aligned_allocate_v) { return m_target.allocate (bytes, alignment); } else { (void)bytes; (void)alignment; throw _alignment_unsupported (); } } void deallocate (void *ptr, size_t bytes) override { m_target.deallocate (ptr, bytes); } void deallocate (void *ptr, size_t bytes, size_t alignment) override { if constexpr (has_aligned_allocate_v) { m_target.deallocate (ptr, bytes, alignment); } else { (void)ptr; (void)bytes; (void)alignment; throw _alignment_unsupported (); } } const std::byte* begin (void) const override { return m_target.begin (); } std::byte* begin (void) override { return m_target.begin (); } std::byte* end (void) override { return m_target.end (); } const std::byte* end (void) const override { return m_target.end (); } size_t offset (const void *ptr) const override { return m_target.offset (ptr); } void reset (void) override { return m_target.reset (); } // capacity queries size_t capacity (void) const override { return m_target.capacity (); } size_t used (void) const override { return m_target.used (); } size_t remain (void) const override { return m_target.remain (); } private: ChildT m_target; }; dynamic (std::unique_ptr _child): m_child (std::move (_child)) { ; } std::unique_ptr m_child; }; } #endif