Skip to content

Instantly share code, notes, and snippets.

@flamewing
Last active February 20, 2020 13:51
Show Gist options
  • Save flamewing/a5ae461880c7c70a2aa75f607c48e802 to your computer and use it in GitHub Desktop.
Save flamewing/a5ae461880c7c70a2aa75f607c48e802 to your computer and use it in GitHub Desktop.
Non-owning version of unique_ptr with identical interface (including array specialization)
/* -*- 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