#ifndef _TILE_BASE_NEVER_DESTROYED_H
#define _TILE_BASE_NEVER_DESTROYED_H

#pragma once
#include <new>
#include <type_traits>
#include <utility>

namespace tile {
namespace detail {
template<typename T>
class NeverDestroyedImpl {
public:
    NeverDestroyedImpl(const NeverDestroyedImpl &)            = delete;
    NeverDestroyedImpl &operator=(const NeverDestroyedImpl &) = delete;

    T *Get() { return reinterpret_cast<T *>(&storage_); }

    const T *Get() const { return reinterpret_cast<const T *>(&storage_); }

    T *operator->() { return Get(); }

    const T *operator->() const { return Get(); }

    T &operator*() { return *Get(); }

    const T &operator*() const { return *Get(); }

protected:
    NeverDestroyedImpl() = default;

protected:
    typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
};
}// namespace detail

template<class T>
class NeverDestroyed final : private detail::NeverDestroyedImpl<T> {
    using Impl = detail::NeverDestroyedImpl<T>;

public:
    template<class... Ts>
    explicit NeverDestroyed(Ts &&...args)
    {
        new (&this->storage_) T(std::forward<Ts>(args)...);
    }

    using Impl::Get;
    using Impl::operator->;
    using Impl::operator*;
};

// Same as `NeverDestroyed`, except that it's constructor is only accessible to
// `T`. This class is useful when `T` is intended to be used as singleton.
template<class T>
class NeverDestroyedSingleton final : private tile::detail::NeverDestroyedImpl<T> {
    using Impl = detail::NeverDestroyedImpl<T>;

public:
    using Impl::Get;
    using Impl::operator->;
    using Impl::operator*;

private:
    friend T;

    template<class... Ts>
    explicit NeverDestroyedSingleton(Ts &&...args)
    {
        new (&this->storage_) T(std::forward<Ts>(args)...);
    }
};

}// namespace tile

#endif// _TILE_BASE_NEVER_DESTROYED_H
