diff --git a/ctl/unique_ptr.cc b/ctl/unique_ptr.cc new file mode 100644 index 000000000..daeda3b1b --- /dev/null +++ b/ctl/unique_ptr.cc @@ -0,0 +1,19 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "unique_ptr.h" diff --git a/ctl/unique_ptr.h b/ctl/unique_ptr.h new file mode 100644 index 000000000..179beda12 --- /dev/null +++ b/ctl/unique_ptr.h @@ -0,0 +1,156 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef COSMOPOLITAN_CTL_UNIQUE_PTR_H_ +#define COSMOPOLITAN_CTL_UNIQUE_PTR_H_ +#include <__utility/forward.h> +#include <__utility/move.h> +#include <__utility/swap.h> + +namespace ctl { + +template<typename T> +struct default_delete +{ + constexpr void operator()(T* p) const noexcept + { + delete p; + } +}; + +template<typename T, typename D = default_delete<T>> +struct unique_ptr +{ + using pointer = T*; + using element_type = T; + using deleter_type = D; + + pointer p; + [[no_unique_address]] deleter_type d; + + constexpr unique_ptr(nullptr_t = nullptr) noexcept : p(nullptr) + { + } + + constexpr unique_ptr(pointer p) noexcept : p(p) + { + } + + constexpr unique_ptr(pointer p, auto&& d) noexcept + : p(p), d(std::forward<decltype(d)>(d)) + { + } + + constexpr unique_ptr(unique_ptr&& u) noexcept : p(u.p), d(std::move(u.d)) + { + u.p = nullptr; + } + + // TODO(mrdomino): + // template <typename U, typename E> + // unique_ptr(unique_ptr<U, E>&& u) noexcept; + + unique_ptr(const unique_ptr&) = delete; + + inline ~unique_ptr() /* noexcept */ + { + reset(); + } + + inline unique_ptr& operator=(unique_ptr r) noexcept + { + swap(r); + return *this; + } + + inline pointer release() noexcept + { + pointer r = p; + p = nullptr; + return r; + } + + inline void reset(nullptr_t = nullptr) noexcept + { + if (p) + d(p); + p = nullptr; + } + + template<typename U> + // TODO(mrdomino): + /* requires is_convertible_v<U, T> */ + inline void reset(U* p2) + { + if (p) { + d(p); + } + p = static_cast<pointer>(p2); + } + + inline void swap(unique_ptr& r) noexcept + { + using std::swap; + swap(p, r.p); + swap(d, r.d); + } + + inline pointer get() const noexcept + { + return p; + } + + inline deleter_type& get_deleter() noexcept + { + return d; + } + + inline const deleter_type& get_deleter() const noexcept + { + return d; + } + + inline explicit operator bool() const noexcept + { + return p; + } + + inline element_type& operator*() const + noexcept(noexcept(*std::declval<pointer>())) + { + if (!p) + __builtin_trap(); + return *p; + } + + inline pointer operator->() const noexcept + { + if (!p) + __builtin_trap(); + return p; + } +}; + +template<typename T, typename... Args> +inline unique_ptr<T> +make_unique(Args&&... args) +{ + return unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + +template<typename T> +inline unique_ptr<T> +make_unique_for_overwrite() +{ +#if 0 + // You'd think that it'd work like this, but std::unique_ptr does not. + return unique_ptr<T>( + static_cast<T*>(::operator new(sizeof(T), align_val_t(alignof(T))))); +#else + return unique_ptr<T>(new T); +#endif +} + +// TODO(mrdomino): specializations for T[] + +} // namespace ctl +#endif // COSMOPOLITAN_CTL_UNIQUE_PTR_H_ diff --git a/test/ctl/unique_ptr_test.cc b/test/ctl/unique_ptr_test.cc new file mode 100644 index 000000000..4d6699b25 --- /dev/null +++ b/test/ctl/unique_ptr_test.cc @@ -0,0 +1,217 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ctl/unique_ptr.h" + +#include <type_traits> + +#include "libc/runtime/runtime.h" + +// #include <memory> +// #define ctl std + +template<typename T, typename D = ctl::default_delete<T>> +using Ptr = ctl::unique_ptr<T, D>; + +template<typename T, typename... Args> +Ptr<T> +Mk(Args&&... args) +{ + return ctl::make_unique<T, Args...>(std::forward<Args>(args)...); +} + +template<typename T> +Ptr<T> +MkRaw() +{ + return ctl::make_unique_for_overwrite<T>(); +} + +#undef ctl + +static int g = 0; + +struct SetsGDeleter +{ + void operator()(auto*) const noexcept + { + ++g; + } +}; + +struct StatefulDeleter +{ + char state; + void operator()(auto*) const noexcept + { + } +}; + +struct FinalDeleter final +{ + void operator()(auto*) const noexcept + { + } +}; + +static_assert(sizeof(Ptr<int, SetsGDeleter>) == sizeof(int*)); + +// not everyone uses [[no_unique_address]]... +static_assert(!std::is_same_v<Ptr<int>, ctl::unique_ptr<int>> || + sizeof(Ptr<int, FinalDeleter>) == sizeof(int*)); + +struct SetsGCtor +{ + SetsGCtor() + { + ++g; + } +}; + +struct SetsGDtor +{ + ~SetsGDtor() + { + ++g; + } +}; + +int +main() +{ + { + Ptr<int> x(new int(5)); + } + + { + Ptr<int, SetsGDeleter> x(new int()); + x.reset(); + if (g != 1) + return 1; + } + + { + g = 0; + Ptr<int, SetsGDeleter> x(new int()); + delete x.release(); + x.reset(); + if (g) + return 17; + } + + { + Ptr<int> x(new int(5)), y(new int(6)); + x.swap(y); + if (*x != 6 || *y != 5) + return 2; + } + + { + Ptr<int> x; + if (x) + return 3; + x.reset(new int(5)); + if (!x) + return 4; + } + + { + g = 0; + Ptr<SetsGCtor> x; + if (g) + return 5; + x = Mk<SetsGCtor>(); + if (g != 1) + return 6; + } + + { + g = 0; + auto x = Mk<SetsGDtor>(); + if (g) + return 7; + x.reset(); + if (g != 1) + return 8; + if (x) + return 9; + } + + { + g = 0; + Ptr<SetsGDtor> x, y; + x = Mk<SetsGDtor>(); + y = Mk<SetsGDtor>(); +#if 0 + // shouldn't compile + x = y; +#endif + x = std::move(y); + if (g != 1) + return 10; + if (y) + return 11; + } + + { + g = 0; + { + auto x = Mk<SetsGDtor>(); + } + if (g != 1) + return 12; + } + + { + g = 0; + { + auto x = Mk<SetsGDtor>(); + x.release(); + } + if (g) + return 13; + } + +#if 0 + // I could not figure out how to test make_unique_for_overwrite. The only + // side effects it has are illegal to detect? + { + g = 0; + auto x = MkRaw<DefaultInitialized>(); + if (g) + return 14; + x.reset(); + if (g) + return 15; + x = Mk<DefaultInitialized>(); + if (g != 1) + return 16; + } +#endif + + { + int a; + // Should compile. + Ptr<int, FinalDeleter> x(&a); + Ptr<int, StatefulDeleter> y(&a); + } + + // next is 18 + + return 0; +}