Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

308 lines
8.4 KiB
C++

// Copyright ⓒ 2018-2021 ThePhD.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See https://github.com/ThePhD/out_ptr/blob/master/docs/out_ptr.adoc for documentation.
#pragma once
#ifndef ZTD_HANDLE_HANDLE_HPP
#define ZTD_HANDLE_HANDLE_HPP
#include <ztd/out_ptr/pointer_of.hpp>
#include <type_traits>
#include <utility>
#include <memory>
namespace ztd {
template <typename T>
struct default_handle_deleter {
using pointer = T;
static void write_null(pointer& p) noexcept {
p = T();
}
static bool is_null(const pointer& p) noexcept {
return p == T();
}
void operator()(const pointer&) const noexcept {
// no-op: override the default deleter
// for handle-specific behavior
}
};
template <typename T>
struct default_handle_deleter<T*> : std::default_delete<T> {
using pointer = T*;
static void write_null(pointer& p) noexcept {
p = pointer();
}
static bool is_null(const pointer& p) noexcept {
return p == pointer();
}
};
namespace op_detail {
struct has_write_null {
template <typename T, typename P,
typename U = typename std::remove_const<typename std::remove_reference<T>::type>::type,
typename Y = decltype(std::declval<U&>().write_null(std::declval<P&>()))>
static std::true_type test(int);
template <typename...>
static std::false_type test(...);
};
struct has_is_null {
template <typename T, typename P,
typename U = typename std::remove_const<typename std::remove_reference<T>::type>::type,
typename Y = decltype(std::declval<U&>().is_null(std::declval<P&>()))>
static std::true_type test(int);
template <typename...>
static std::false_type test(...);
};
template <typename D, typename P>
void write_null(std::true_type, D& deleter, P&& p) noexcept {
deleter.write_null(std::forward<P>(p));
}
template <typename D, typename P>
void write_null(std::false_type, D&, P&& p) noexcept {
using X = typename std::remove_const<typename std::remove_reference<P>::type>::type;
default_handle_deleter<X>::write_null(std::forward<P>(p));
}
template <typename D, typename P>
void write_null(D& deleter, P&& p) noexcept {
using yes_no = decltype(has_write_null::test<D, typename std::remove_const<typename std::remove_reference<P>::type>::type>(0));
write_null(yes_no(), deleter, std::forward<P>(p));
}
template <typename D, typename P>
bool is_null(std::true_type, D& deleter, P&& p) noexcept {
return deleter.is_null(std::forward<P>(p));
}
template <typename D, typename P>
bool is_null(std::false_type, D&, P&& p) noexcept {
return default_handle_deleter<typename std::remove_const<typename std::remove_reference<P>::type>::type>::is_null(std::forward<P>(p));
}
template <typename D, typename P>
bool is_null(D& deleter, P&& p) noexcept {
using yes_no = decltype(has_is_null::test<D, typename std::remove_const<typename std::remove_reference<P>::type>::type>(0));
return is_null(yes_no(), deleter, std::forward<P>(p));
}
} // namespace op_detail
template <typename T,
typename Dx = default_handle_deleter<T>>
struct handle : Dx {
public:
using pointer = ztd::out_ptr::pointer_type_t<T, Dx>;
using deleter_type = Dx;
private:
using deleter_base = deleter_type;
pointer res;
public:
handle() noexcept {
}
handle(pointer h) noexcept
: res(h) {
}
handle(std::nullptr_t) noexcept {
deleter_type& deleter = get_deleter();
op_detail::write_null(deleter, res);
}
handle(pointer h, deleter_type d) noexcept
: deleter_base(std::move(d)), res(h) {
}
handle(std::nullptr_t, deleter_type d) noexcept
: deleter_base(std::move(d)) {
deleter_type& deleter = get_deleter();
op_detail::write_null(deleter, res);
}
template <typename... DxArgs>
handle(pointer h, DxArgs&&... dx_args) noexcept
: deleter_base(std::forward<DxArgs>(dx_args)...), res(h) {
}
template <typename... DxArgs>
handle(std::nullptr_t, DxArgs&&... dx_args) noexcept
: deleter_base(std::forward<Dx>(dx_args)...) {
deleter_type& deleter = get_deleter();
op_detail::write_null(deleter, res);
}
handle(const handle& nocopy) noexcept = delete;
handle(handle&& mov) noexcept
: deleter_base(std::move(mov)), res(std::move(mov.res)) {
deleter_type& deleter = get_deleter();
mov.reset(mov.get_null());
}
handle& operator=(const handle&) noexcept = delete;
handle& operator=(handle&& right) noexcept {
this->reset(right.release());
return *this;
}
handle& operator=(pointer right) noexcept {
this->reset(right);
return *this;
}
handle& operator=(std::nullptr_t) noexcept {
this->reset(nullptr);
return *this;
}
explicit operator bool() const noexcept {
return this->is_null();
}
pointer get_null() const noexcept {
pointer p;
const deleter_type& deleter = this->get_deleter();
op_detail::write_null(deleter, p);
return p;
}
static pointer get_null(const deleter_type& deleter) noexcept {
pointer p;
op_detail::write_null(deleter, p);
return p;
}
bool is_null() const noexcept {
const deleter_type& deleter = this->get_deleter();
return op_detail::is_null(deleter, res);
}
static bool is_null(const deleter_type& deleter, pointer& res) noexcept {
return op_detail::is_null(deleter, res);
}
pointer& get() noexcept {
return res;
}
const pointer& get() const noexcept {
return res;
}
void reset(pointer h) noexcept {
deleter_type& deleter = this->get_deleter();
if (!is_null())
deleter(res);
res = h;
}
void reset(std::nullptr_t = nullptr) noexcept {
deleter_type& deleter = this->get_deleter();
if (!is_null())
deleter(res);
op_detail::write_null(deleter, res);
}
pointer release() noexcept {
pointer rel = std::move(res);
deleter_type& deleter = this->get_deleter();
op_detail::write_null(deleter, res);
return rel;
}
void swap(handle& other) noexcept {
std::swap(other.get_deleter(), this->get_deleter());
std::swap(other.res, res);
}
deleter_type& get_deleter() noexcept {
deleter_type& deleter = *this;
return deleter;
}
const deleter_type& get_deleter() const noexcept {
const deleter_type& deleter = *this;
return deleter;
}
pointer operator*() noexcept {
return get();
}
pointer operator->() noexcept {
return get();
}
~handle() noexcept {
deleter_type& deleter = this->get_deleter();
if (!is_null())
deleter(res);
op_detail::write_null(deleter, res);
}
};
template <typename T, typename Dx>
inline bool operator==(const handle<T, Dx>& left, const handle<T, Dx>& right) noexcept {
return left.get() == right.get();
}
template <typename T, typename Dx>
inline bool operator==(std::nullptr_t, const handle<T, Dx>& left) noexcept {
return left.is_null();
}
template <typename T, typename Dx>
inline bool operator==(const handle<T, Dx>& left, std::nullptr_t) noexcept {
return left.is_null();
}
template <typename T, typename Dx>
inline bool operator==(typename handle<T, Dx>::pointer right, const handle<T, Dx>& left) noexcept {
return left.get() == right;
}
template <typename T, typename Dx>
inline bool operator==(const handle<T, Dx>& left, typename handle<T, Dx>::pointer right) noexcept {
return left.get() == right;
}
template <typename T, typename Dx>
inline bool operator!=(const handle<T, Dx>& left, const handle<T, Dx>& right) noexcept {
return left.get() != right.get();
}
template <typename T, typename Dx>
inline bool operator!=(std::nullptr_t right, const handle<T, Dx>& left) noexcept {
return left.get() != left.get_null();
}
template <typename T, typename Dx>
inline bool operator!=(const handle<T, Dx>& left, std::nullptr_t right) noexcept {
return left.get() != left.get_null();
}
template <typename T, typename Dx>
inline bool operator!=(typename handle<T, Dx>::pointer right, const handle<T, Dx>& left) noexcept {
return left.get() != right;
}
template <typename T, typename Dx>
inline bool operator!=(const handle<T, Dx>& left, typename handle<T, Dx>::pointer right) noexcept {
return left.get() != right;
}
} // namespace ztd
#endif // ZTD_HANDLE_HANDLE_HPP