#ifndef IMATH_CONTAINER_ALLOCATOR_HPP
#define IMATH_CONTAINER_ALLOCATOR_HPP
#include "IMathLib/utility/iterator.hpp"
#include "IMathLib/utility/functional.hpp"
#include "IMathLib/math/math/numeric_traits.hpp"
// コンテナ等で用いるためのアロケータの実装
namespace iml {
// allocator_traitsのための補助
namespace alloc {
// allocatorにpointerが存在
template <class, class = void>
struct is_exist_pointer : false_type {};
template <class T>
struct is_exist_pointer<T, void_t<typename T::pointer>> : true_type {};
template <class Allocator, class T, bool = is_exist_pointer<Allocator>::value>
struct pointer {
using type = typename Allocator::pointer;
};
template <class Allocator, class T>
struct pointer<Allocator, T, false> {
using type = T;
};
// allocatorにdifference_typeが存在
template <class, class = void>
struct is_exist_difference_type : false_type {};
template <class T>
struct is_exist_difference_type<T, void_t<typename T::difference_type>> : true_type {};
template <class Allocator, class T, bool = is_exist_difference_type<Allocator>::value>
struct difference_type {
using type = typename Allocator::difference_type;
};
template <class Allocator, class T>
struct difference_type<Allocator, T, false> {
using type = T;
};
// allocatorにsize_typeが存在
template <class, class = void>
struct is_exist_size_type : false_type {};
template <class T>
struct is_exist_size_type<T, void_t<typename T::size_type>> : true_type {};
template <class Allocator, class T, bool = is_exist_size_type<Allocator>::value>
struct size_type {
using type = typename Allocator::size_type;
};
template <class Allocator, class T>
struct size_type<Allocator, T, false> {
using type = T;
};
// max_size()が存在するならそれを呼び出す
template <class Allocator>
struct is_exist_max_size {
private:
template <class UAlloc> static auto tester(UAlloc*) ->decltype(declval<UAlloc>().max_size(), true_type());
template <class UAlloc> static false_type tester(...);
public:
static constexpr bool value = decltype(tester<Allocator>(nullptr))::value;
};
template <class Allocator, class SizeType, class ValueType, bool = is_exist_max_size<Allocator>::value>
struct Max_size {
static constexpr SizeType max_size(const Allocator& a) { return a.max_size(); }
};
template <class Allocator, class SizeType, class ValueType>
struct Max_size<Allocator, SizeType, ValueType, false> {
static constexpr SizeType max_size(const Allocator&) { return (numeric_traits<SizeType>::max)() / sizeof(ValueType); }
};
// construct()が存在するならそれを呼び出す
template <class, class, class>
struct is_exist_construct {};
template <class Allocator, class T, class... Types>
struct is_exist_construct<Allocator, T, type_tuple<Types...>> {
private:
template <class UAlloc> static auto tester(UAlloc*) ->decltype(declval<UAlloc>().construct(declval<T*>(), declval<Types>()...), true_type());
template <class UAlloc> static false_type tester(...);
public:
static constexpr bool value = decltype(tester<Allocator>(nullptr))::value;
};
template <class Allocator, class T, class Types, bool = is_exist_construct<Allocator, T, Types>::value>
struct Construct {};
template <class Allocator, class T, class... Types>
struct Construct<Allocator, T, type_tuple<Types...>, true> {
static constexpr void construct(Allocator& a, T* p, Types&&... args) { a.construct(p, forward<Types>(args)...); }
};
template <class Allocator, class T, class... Types>
struct Construct<Allocator, T, type_tuple<Types...>, false> {
static constexpr void construct(Allocator& a, T* p, Types&&... args) { ::new (static_cast<void*>(p)) T(forward<Types>(args)...); }
};
// destroy()が存在するならそれを呼び出す
template <class Allocator, class T>
struct is_exist_destroy {
private:
template <class UAlloc> static auto tester(UAlloc*) ->decltype(declval<UAlloc>().destroy(declval<T*>()), true_type());
template <class UAlloc> static false_type tester(...);
public:
static constexpr bool value = decltype(tester<Allocator>(nullptr))::value;
};
template <class Allocator, class T, bool = is_exist_destroy<Allocator, T>::value>
struct Destroy {
static void destroy(Allocator& a, T* p) { a.destroy(p); }
};
template <class Allocator, class T>
struct Destroy<Allocator, T, false> {
static void destroy(Allocator&, T* p) { p->~T(); }
};
// select_on_container_copy_construction()が存在するならそれを呼び出す
template <class Allocator>
struct is_exist_select_on_container_copy_construction {
private:
template <class UAlloc> static auto tester(UAlloc*) ->decltype(declval<UAlloc>().select_on_container_copy_construction(), true_type());
template <class UAlloc> static false_type tester(...);
public:
static constexpr bool value = decltype(tester<Allocator>(nullptr))::value;
};
template <class Allocator, bool = is_exist_select_on_container_copy_construction<Allocator>::value>
struct Select_on_container_copy_construction {
static constexpr Allocator select_on_container_copy_construction(const Allocator& a) { return a.select_on_container_copy_construction(); }
};
template <class Allocator>
struct Select_on_container_copy_construction<Allocator, false> {
static constexpr Allocator select_on_container_copy_construction(const Allocator&) { return Allocator(); }
};
}
template <class Allocator>
struct allocator_traits {
using allocator_type = Allocator;
using value_type = typename Allocator::value_type;
using pointer = typename alloc::pointer<Allocator, value_type*>::type;
using difference_type = typename alloc::difference_type<Allocator, typename pointer_traits<pointer>::difference_type>::type;
using size_type = typename alloc::size_type<Allocator, make_unsigned_t<difference_type>>::type;
template <class Other>
using rebind = typename Allocator::template rebind<Other>;
template <class Other>
using rebind_t = typename Allocator::template rebind_t<Other>;
// メモリのアロケート
[[nodiscard]] static constexpr pointer allocate(allocator_type& a, size_t n) { return a.allocate(n); }
// メモリのディアロケート
static constexpr void deallocate(allocator_type& a, pointer p, size_t n) { return a.deallocate(p, n); }
// 一度に確保可能なメモリの数
static constexpr size_type max_size(const allocator_type& a) noexcept{
return alloc::Max_size<allocator_type, size_type, value_type>::max_size(a);
}
// コンストラクタの適用
template <class T, class... Types>
static constexpr void construct(allocator_type& a, T* p, Types&&... args) {
alloc::Construct<allocator_type, T, type_tuple<Types...>>::construct(a, p, forward<Types>(args)...);
}
template <class InputIterator, class... Types>
static constexpr void construct_all(allocator_type& a, InputIterator first, InputIterator last, Types&&... args) {
// 入力イテレータでなければならない
static_assert(is_iterator_v<InputIterator, input_iterator_tag>, "The type of iterator is different.");
while (first != last) construct(a, to_address(first++), forward<Types>(args)...);
}
template <class InputIterator1, class InputIterator2>
static constexpr void copy_construct(allocator_type& a, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) {
// 入力イテレータでなければならない
static_assert(is_iterator_v<InputIterator1, input_iterator_tag> && is_iterator_v<InputIterator2, input_iterator_tag>, "The type of iterator is different.");
while (first1 != last1) {
if (first2 == last2) break;
construct(a, to_address(first1++), *to_address(first2++));
}
// 足りない部分はデフォルトコンストラクタを適用する
while (first1 != last1) construct(a, to_address(first1++));
}
template <class InputIterator1, class InputIterator2>
static constexpr void move_construct(allocator_type& a, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) {
// 入力イテレータでなければならない
static_assert(is_iterator_v<InputIterator1, input_iterator_tag> && is_iterator_v<InputIterator2, input_iterator_tag>, "The type of iterator is different.");
while (first1 != last1) {
if (first2 == last2) break;
construct(a, to_address(first1++), move(*to_address(first2++)));
}
while (first1 != last1) construct(a, to_address(first1++));
}
// デストラクタの適用
template <class T>
static void destroy(allocator_type& a, T* p) {
alloc::Destroy<allocator_type, T>::destroy(a, p);
}
template <class OutputIterator>
static void destroy(allocator_type& a, OutputIterator first, OutputIterator last) {
// 出力イテレータでなければならない
static_assert(is_iterator_v<OutputIterator, output_iterator_tag>, "The type of iterator is different.");
while (first != last) destroy(a, to_address(first++));
}
// コンテナのコピー構築に使用するアロケータオブジェクトを取得
static constexpr allocator_type select_on_container_copy_construction(const allocator_type& a) {
return alloc::Select_on_container_copy_construction<allocator_type>::select_on_container_copy_construction(a);
}
};
// アロケータ
template <class T>
class allocator {
public:
constexpr allocator() noexcept {}
constexpr allocator(allocator&) noexcept {}
template <class U>
constexpr allocator(allocator<U>&) noexcept {}
~allocator() {}
using value_type = T;
using pointer = T * ;
template <class Other>
struct rebind {
using other = allocator<Other>;
};
template <class Other>
using rebind_t = allocator<Other>;
// メモリ確保(new[]で確保しないためdeleteで解放できる)
[[nodiscard]] pointer allocate(size_t n) { return static_cast<pointer>(::operator new(n * sizeof(value_type))); }
// メモリ解放
void deallocate(pointer p, size_t) { ::operator delete(static_cast<void*>(p)); }
};
template <class T, class U>
bool operator==(const allocator<T>&, const allocator<U>&) { return true; }
template <class T, class U, class Iterator1, class Iterator2>
bool operator!=(const allocator<T>&, const allocator<U>&) { return false; }
// リソースの破棄条件をdeallocator_baseを通して共通化する
namespace dealloc {
static constexpr size_t variable = 0; // newで確保されたインスタンスに対してdeleteをする
static constexpr size_t array = 1; // new[]で確保されたインスタンスに対してdelete[]をする
static constexpr size_t allocator = 2; // アロケータによって確保されたインスタンス
static constexpr size_t function = 3; // void(T*)な関数を用いてインスタンスを破棄する
}
// ディアロケータ用のプレースホルダ
template <size_t N>
using dealloc_ident = index_tuple<size_t, N>;
class deallocator_base {
protected:
void* p_m;
constexpr deallocator_base(void* p) : p_m(p) {}
public:
virtual ~deallocator_base() = 0 {}
// ホールドしている変数の解放
virtual void dispose(void* p = nullptr) = 0;
// インスタンスと自身のリソースを解放
virtual void destroy() = 0;
// インスタンスの監視を止め,自身のリソースを解放
virtual void release() = 0;
};
template <class, size_t>
class deallocator;
// newで確保されたインスタンスについて
template <class T>
class deallocator<T, dealloc::variable> : public deallocator_base {
template <class U>
constexpr deallocator(U* p) : deallocator_base(static_cast<void*>(p)) {}
public:
~deallocator() {}
void dispose(void* p) { delete static_cast<T*>(p_m); p_m = static_cast<void*>(p); }
void destroy() { delete static_cast<T*>(p_m); delete this; }
void release() { delete this; }
using value_type = T;
template <class U>
static deallocator* get(U* p) { return new deallocator(p); }
};
// new[]で確保されたインスタンスについて
template <class T>
class deallocator<T, dealloc::array> : public deallocator_base {
template <class U>
constexpr deallocator(U* p) : deallocator_base(static_cast<void*>(p)) {}
public:
~deallocator() {}
void dispose(void* p) { delete[] static_cast<T*>(p_m); p_m = static_cast<void*>(p); }
void destroy() { delete[] static_cast<T*>(p_m); delete this; }
void release() { delete this; }
using value_type = T;
template <class U>
static deallocator* get(U* p) { return new deallocator(p); }
};
// アロケータにより確保されたインスタンスについて
template <class Allocator>
class deallocator<Allocator, dealloc::allocator> : public deallocator_base {
public:
using value_type = typename Allocator::value_type;
private:
Allocator* alloc_m;
size_t size_m; // アロケータによってアロケートされている要素数
template <class U>
constexpr deallocator(U* p, Allocator& alloc, size_t n) : deallocator_base(static_cast<void*>(p)), alloc_m(addressof(alloc)), size_m(n) {}
public:
~deallocator() {}
void dispose(void* p) {
allocator_traits<Allocator>::destroy(*alloc_m, static_cast<value_type*>(p_m));
alloc_m->deallocate(static_cast<value_type*>(p_m), size_m);
p_m = static_cast<void*>(p);
}
void destroy() {
allocator_traits<Allocator>::destroy(*alloc_m, static_cast<value_type*>(p_m));
alloc_m->deallocate(static_cast<value_type*>(p_m), size_m);
delete this;
}
void release() { delete this; }
template <class U>
static deallocator* get(U* p, Allocator& alloc, size_t n) { return new deallocator(p, alloc, n); }
};
// 特定の関数を用いて破棄するインスタンスについて
template <class T>
class deallocator<T, dealloc::function> : public deallocator_base {
function<void(T*)> f_m;
template <class U, class F>
constexpr deallocator(U* p, F f) : deallocator_base(static_cast<void*>(p)), f_m(f) {}
public:
~deallocator() {}
void dispose(void* p) { f_m(static_cast<T*>(p_m)); p_m = static_cast<void*>(p); }
void destroy() { f_m(static_cast<T*>(p_m)); delete this; }
void release() { delete this; }
using value_type = T;
template <class U, class F>
static deallocator* get(U* p, F f) { return new deallocator(p, f); }
};
}
#endif