Last active
February 20, 2020 13:51
-
-
Save flamewing/a5ae461880c7c70a2aa75f607c48e802 to your computer and use it in GitHub Desktop.
Non-owning version of unique_ptr with identical interface (including array specialization)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* -*- Mode: C++; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ | |
/* | |
* Copyright (C) Flamewing 2018 <flamewing.sonic@gmail.com> | |
* | |
* This program is free software: you can redistribute it and/or modify it | |
* under the terms of the GNU Lesser General Public License as published | |
* by the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but | |
* WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
* See the GNU Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#ifndef DUMB_PTR_H | |
#define DUMB_PTR_H 1 | |
#include <functional> | |
#include <type_traits> | |
#include <utility> | |
namespace nonstd { | |
template <typename Tp> | |
class dumb_ptr { | |
public: | |
using element_type = Tp; | |
using pointer = std::add_pointer_t<Tp>; | |
using reference = std::add_lvalue_reference_t<Tp>; | |
template <typename Up> | |
using safe_conversion = std::integral_constant<bool, std::is_convertible<typename std::add_pointer_t<Up>, pointer>::value && !std::is_array<Up>::value>; | |
constexpr dumb_ptr() noexcept = default; | |
// copying c’tors | |
constexpr dumb_ptr(const dumb_ptr& p) = default; | |
constexpr auto operator=(const dumb_ptr& p) -> dumb_ptr& = default; | |
constexpr dumb_ptr(dumb_ptr&& p) noexcept = default; | |
constexpr auto operator=(dumb_ptr&& p) noexcept -> dumb_ptr& = default; | |
~dumb_ptr() = default; | |
// pointer-accepting c’tors | |
constexpr explicit dumb_ptr(std::nullptr_t) noexcept : ptr() {} | |
constexpr explicit dumb_ptr(pointer p) noexcept : ptr(p) {} | |
// dumb_ptr converting c’tors | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr explicit dumb_ptr(const dumb_ptr<Up>& p) noexcept : ptr(p.get()) {} | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr auto operator=(const dumb_ptr<Up>& p) noexcept -> dumb_ptr& { | |
reset(p.get()); | |
return *this; | |
} | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr explicit dumb_ptr(dumb_ptr<Up>&& p) noexcept : ptr(p.get()) {} | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr auto operator=(dumb_ptr<Up>&& p) noexcept -> dumb_ptr& { | |
reset(p.get()); | |
return *this; | |
} | |
// Observers. | |
// Dereference the stored pointer. | |
constexpr auto operator*() const -> reference { | |
return *get(); | |
} | |
// Return the stored pointer. | |
constexpr auto operator->() const noexcept -> pointer { | |
return get(); | |
} | |
// Return the stored pointer. | |
constexpr auto get() const noexcept -> pointer { | |
return ptr; | |
} | |
// Return true if the stored pointer is not null. | |
constexpr explicit operator bool() const noexcept { | |
return get() != nullptr; | |
} | |
// Modifiers. | |
// Release view of any stored pointer. | |
constexpr auto release() noexcept -> pointer { | |
pointer p = get(); | |
reset(); | |
return p; | |
} | |
// Replace the stored pointer. | |
constexpr void reset(pointer p = nullptr) noexcept { | |
ptr = p; | |
} | |
// Exchange the pointer with another object. | |
constexpr void swap(dumb_ptr& u) noexcept { | |
std::swap(ptr, u.ptr); | |
} | |
private: | |
pointer ptr = nullptr; | |
}; | |
/// dumb_ptr for array objects with a runtime length | |
template <typename Tp> | |
class dumb_ptr<Tp[]> { | |
public: | |
using element_type = Tp; | |
using pointer = std::add_pointer_t<Tp>; | |
using reference = std::add_lvalue_reference_t<Tp>; | |
template<typename Up, typename Up_up = dumb_ptr<Up>, typename Up_element_type = typename Up_up::element_type> | |
using safe_conversion = std::integral_constant<bool, | |
std::is_array<Up>::value && | |
std::is_same<pointer, element_type*>::value && | |
std::is_same<typename Up_up::pointer, Up_element_type*>::value && | |
std::is_convertible<Up_element_type(*)[], element_type(*)[]>::value | |
>; | |
template<typename Up> | |
using safe_conversion_raw = std::integral_constant<bool, | |
(std::is_same<Up, pointer>::value || std::is_same<Up, std::nullptr_t>::value) || | |
(std::is_pointer<Up>::value && std::is_same<pointer, element_type*>::value && | |
std::is_convertible<std::remove_pointer_t<Up>(*)[], element_type(*)[]>::value) | |
>; | |
constexpr dumb_ptr() noexcept = default; | |
// copying c’tors | |
constexpr dumb_ptr(const dumb_ptr& p) = default; | |
constexpr auto operator=(const dumb_ptr& p) -> dumb_ptr& = default; | |
constexpr dumb_ptr(dumb_ptr&& p) noexcept = default; | |
constexpr auto operator=(dumb_ptr&& p) noexcept -> dumb_ptr& = default; | |
~dumb_ptr() noexcept = default; | |
// pointer-accepting c’tors | |
constexpr explicit dumb_ptr(std::nullptr_t) noexcept : ptr() {} | |
template<typename Up, typename = typename std::enable_if<safe_conversion_raw<Up>::value, bool>::type> | |
constexpr explicit dumb_ptr(Up p) noexcept : ptr(p) {} | |
// dumb_ptr converting c’tors | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr explicit dumb_ptr(const dumb_ptr<Up>& p) noexcept : ptr(p.get()) {} | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr auto operator=(const dumb_ptr<Up>& p) noexcept -> dumb_ptr& { | |
reset(p.get()); | |
return *this; | |
} | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr explicit dumb_ptr(dumb_ptr<Up>&& p) noexcept : ptr(p.get()) {} | |
template <typename Up, typename = std::enable_if_t<safe_conversion<Up>::value>> | |
constexpr auto operator=(dumb_ptr<Up>&& p) noexcept -> dumb_ptr& { | |
reset(p.get()); | |
return *this; | |
} | |
// Observers. | |
/// Access an element of owned array. | |
constexpr auto operator[](size_t i) const noexcept -> reference { | |
return get()[i]; | |
} | |
// Return the stored pointer. | |
constexpr auto get() const noexcept -> pointer { | |
return ptr; | |
} | |
// Return true if the stored pointer is not null. | |
constexpr explicit operator bool() const noexcept { | |
return get() != nullptr; | |
} | |
// Modifiers. | |
// Release view of any stored pointer. | |
constexpr auto release() noexcept -> pointer { | |
pointer p = get(); | |
reset(); | |
return p; | |
} | |
// Replace the stored pointer. | |
template <typename Up, | |
typename = std::enable_if_t< | |
std::is_same<Up, pointer>::value || | |
(std::is_same<pointer, element_type*>::value && std::is_pointer<Up>::value && | |
std::is_convertible<std::remove_pointer_t<Up>(*)[], element_type(*)[]>::value)>> | |
constexpr void reset(Up p) noexcept { | |
ptr = p; | |
} | |
constexpr void reset(std::nullptr_t p = nullptr) noexcept { | |
ptr = p; | |
} | |
// Exchange the pointer with another object. | |
constexpr void swap(dumb_ptr& u) noexcept { | |
std::swap(ptr, u.ptr); | |
} | |
private: | |
pointer ptr = nullptr; | |
}; | |
template <typename Tp> | |
constexpr inline void swap(dumb_ptr<Tp>& x, dumb_ptr<Tp>& y) noexcept { | |
x.swap(y); | |
} | |
template <typename Tp, typename Up> | |
constexpr inline auto operator==(const dumb_ptr<Tp>& x, const dumb_ptr<Up>& y) noexcept -> bool { | |
return x.get() == y.get(); | |
} | |
template <typename Tp> | |
constexpr inline auto operator==(const dumb_ptr<Tp>& x, std::nullptr_t) noexcept -> bool { | |
return !x; | |
} | |
template <typename Tp> | |
constexpr inline auto operator==(std::nullptr_t, const dumb_ptr<Tp>& x) noexcept -> bool { | |
return !x; | |
} | |
template <typename Tp, typename Up> | |
constexpr inline auto operator!=(const dumb_ptr<Tp>& x, const dumb_ptr<Up>& y) noexcept -> bool { | |
return x.get() != y.get(); | |
} | |
template <typename Tp> | |
constexpr inline auto operator!=(const dumb_ptr<Tp>& x, std::nullptr_t) noexcept -> bool { | |
return static_cast<bool>(x); | |
} | |
template <typename Tp> | |
constexpr inline auto operator!=(std::nullptr_t, const dumb_ptr<Tp>& x) noexcept -> bool { | |
return static_cast<bool>(x); | |
} | |
template <typename Tp, typename Up> | |
constexpr inline auto operator<(const dumb_ptr<Tp>& x, const dumb_ptr<Up>& y) noexcept -> bool { | |
using CT = typename std::common_type_t<typename dumb_ptr<Tp>::pointer, typename dumb_ptr<Up>::pointer>; | |
return std::less<CT>()(x.get(), y.get()); | |
} | |
template <typename Tp> | |
constexpr inline auto operator<(const dumb_ptr<Tp>& x, std::nullptr_t) noexcept -> bool { | |
return std::less<typename dumb_ptr<Tp>::pointer>()(x.get(), nullptr); | |
} | |
template <typename Tp> | |
constexpr inline auto operator<(std::nullptr_t, const dumb_ptr<Tp>& x) noexcept -> bool { | |
return std::less<typename dumb_ptr<Tp>::pointer>()(nullptr, x.get()); | |
} | |
template <typename Tp, typename Up> | |
constexpr inline auto operator<=(const dumb_ptr<Tp>& x, const dumb_ptr<Up>& y) noexcept -> bool { | |
return !(y < x); | |
} | |
template <typename Tp> | |
constexpr inline auto operator<=(const dumb_ptr<Tp>& x, std::nullptr_t) noexcept -> bool { | |
return !(nullptr < x); | |
} | |
template <typename Tp> | |
constexpr inline auto operator<=(std::nullptr_t, const dumb_ptr<Tp>& x) noexcept -> bool { | |
return !(x < nullptr); | |
} | |
template <typename Tp, typename Up> | |
constexpr inline auto operator>(const dumb_ptr<Tp>& x, const dumb_ptr<Up>& y) noexcept -> bool { | |
return y < x; | |
} | |
template <typename Tp> | |
constexpr inline auto operator>(const dumb_ptr<Tp>& x, std::nullptr_t) noexcept -> bool { | |
return std::less<typename dumb_ptr<Tp>::pointer>()(nullptr, x.get()); | |
} | |
template <typename Tp> | |
constexpr inline auto operator>(std::nullptr_t, const dumb_ptr<Tp>& x) noexcept -> bool { | |
return std::less<typename dumb_ptr<Tp>::pointer>()(x.get(), nullptr); | |
} | |
template <typename Tp, typename Up> | |
constexpr inline auto operator>=(const dumb_ptr<Tp>& x, const dumb_ptr<Up>& y) noexcept -> bool { | |
return !(x < y); | |
} | |
template <typename Tp> | |
constexpr inline auto operator>=(const dumb_ptr<Tp>& x, std::nullptr_t) noexcept -> bool { | |
return !(x < nullptr); | |
} | |
template <typename Tp> | |
constexpr inline auto operator>=(std::nullptr_t, const dumb_ptr<Tp>& x) noexcept -> bool { | |
return !(nullptr < x); | |
} | |
} // namespace nonstd | |
namespace std { | |
/// std::hash specialization for dumb_ptr. | |
template <typename Tp> | |
struct hash<nonstd::dumb_ptr<Tp>> { | |
auto operator()(const nonstd::dumb_ptr<Tp>& u) const noexcept -> size_t { | |
using UP = nonstd::dumb_ptr<Tp>; | |
return std::hash<typename UP::pointer>()(u.get()); | |
} | |
}; | |
} // namespace std | |
#endif // DUMB_PTR_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment