diff --git a/ctl/compressed_pair.cc b/ctl/compressed_pair.cc new file mode 100644 index 000000000..d5ae56e4c --- /dev/null +++ b/ctl/compressed_pair.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 "compressed_pair.h" diff --git a/ctl/compressed_pair.h b/ctl/compressed_pair.h new file mode 100644 index 000000000..c9ecfde15 --- /dev/null +++ b/ctl/compressed_pair.h @@ -0,0 +1,100 @@ +// -*-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_COMPRESSED_PAIR_H_ +#define COSMOPOLITAN_CTL_COMPRESSED_PAIR_H_ +#include <__type_traits/is_empty.h> +#include <__utility/forward.h> +#include <__utility/move.h> +#include <__utility/swap.h> + +namespace ctl { + +namespace __ { + +template +concept Empty = std::is_empty_v; + +template +struct pair_elem +{ + using value_type = T; + + constexpr pair_elem(auto&& u) : t(std::forward(u)) + { + } + + constexpr T& get() noexcept + { + return t; + } + + constexpr const T& get() const noexcept + { + return t; + } + + private: + T t; +}; + +template +struct pair_elem : private T +{ + using value_type = T; + + constexpr pair_elem(auto&& u) : T(std::forward(u)) + { + } + + constexpr T& get() noexcept + { + return static_cast(*this); + } + + constexpr const T& get() const noexcept + { + return static_cast(*this); + } + + protected: +}; + +} // namespace __ + +template +struct compressed_pair + : private __::pair_elem + , private __::pair_elem +{ + using first_type = __::pair_elem; + using second_type = __::pair_elem; + + constexpr compressed_pair(auto&& a, auto&& b) + : first_type(std::forward(a)) + , second_type(std::forward(b)) + { + } + + constexpr typename first_type::value_type& first() noexcept + { + return static_cast(*this).get(); + } + + constexpr const typename first_type::value_type& first() const noexcept + { + return static_cast(*this).get(); + } + + constexpr typename second_type::value_type& second() noexcept + { + return static_cast(*this).get(); + } + + constexpr const typename second_type::value_type& second() const noexcept + { + return static_cast(*this).get(); + } +}; + +} // namespace ctl +#endif // COSMOPOLITAN_CTL_COMPRESSED_PAIR_H_ 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..a1c83cbf4 --- /dev/null +++ b/ctl/unique_ptr.h @@ -0,0 +1,153 @@ +// -*-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 "compressed_pair.h" + +namespace ctl { + +template +struct default_delete +{ + constexpr void operator()(T* p) const noexcept + { + delete p; + } +}; + +template> +struct unique_ptr +{ + using pointer = T*; + using element_type = T; + using deleter_type = D; + + compressed_pair d; + + constexpr unique_ptr(nullptr_t = nullptr) noexcept : d(nullptr, D{}) + { + } + + constexpr unique_ptr(pointer p) noexcept : d(p, D{}) + { + } + + constexpr unique_ptr(pointer p, auto&& d) noexcept + : d(p, std::forward(d)) + { + } + + constexpr unique_ptr(unique_ptr&& u) noexcept + : d(u.d.first(), std::move(u.d.second())) + { + u.d.first() = nullptr; + } + + // TODO(mrdomino): + // template + // unique_ptr(unique_ptr&& 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 = d.first(); + d.first() = nullptr; + return r; + } + + inline void reset(nullptr_t = nullptr) noexcept + { + if (d.first()) + d.second()(d.first()); + d.first() = nullptr; + } + + template + // TODO(mrdomino): + /* requires is_convertible_v */ + inline void reset(U* p2) + { + if (d.first()) { + d.second()(d.first()); + } + d.first() = static_cast(p2); + } + + inline void swap(unique_ptr& r) noexcept + { + using std::swap; + swap(d, r.d); + } + + inline pointer get() const noexcept + { + return d.first(); + } + + inline deleter_type& get_deleter() noexcept + { + return d.second(); + } + + inline const deleter_type& get_deleter() const noexcept + { + return d.second(); + } + + inline explicit operator bool() const noexcept + { + return d.first(); + } + + inline element_type& operator*() const + noexcept(noexcept(*std::declval())) + { + if (!*this) + __builtin_trap(); + return *d.first(); + } + + inline pointer operator->() const noexcept + { + if (!*this) + __builtin_trap(); + return d.first(); + } +}; + +template +inline unique_ptr +make_unique(Args&&... args) +{ + return unique_ptr(new T(std::forward(args)...)); +} + +template +inline unique_ptr +make_unique_for_overwrite() +{ +#if 0 + // You'd think that it'd work like this, but std::unique_ptr does not. + return unique_ptr( + static_cast(::operator new(sizeof(T), align_val_t(alignof(T))))); +#else + return unique_ptr(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..baa283a27 --- /dev/null +++ b/test/ctl/unique_ptr_test.cc @@ -0,0 +1,191 @@ +// -*- 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 + +#include "libc/runtime/runtime.h" + +// #include +// #define ctl std + +template> +using Ptr = ctl::unique_ptr; + +template +Ptr +Mk(Args&&... args) +{ + return ctl::make_unique(std::forward(args)...); +} + +template +Ptr +MkRaw() +{ + return ctl::make_unique_for_overwrite(); +} + +#undef ctl + +static int g = 0; + +struct SetsGDeleter +{ + void operator()(auto*) const noexcept + { + ++g; + } +}; + +static_assert(sizeof(Ptr) == sizeof(int*)); + +struct SetsGCtor +{ + SetsGCtor() + { + ++g; + } +}; + +struct SetsGDtor +{ + ~SetsGDtor() + { + ++g; + } +}; + +int +main() +{ + { + Ptr x(new int(5)); + } + + { + Ptr x(new int()); + x.reset(); + if (g != 1) + return 1; + } + + { + g = 0; + Ptr x(new int()); + delete x.release(); + x.reset(); + if (g) + return 17; + } + + { + Ptr x(new int(5)), y(new int(6)); + x.swap(y); + if (*x != 6 || *y != 5) + return 2; + } + + { + Ptr x; + if (x) + return 3; + x.reset(new int(5)); + if (!x) + return 4; + } + + { + g = 0; + Ptr x; + if (g) + return 5; + x = Mk(); + if (g != 1) + return 6; + } + + { + g = 0; + auto x = Mk(); + if (g) + return 7; + x.reset(); + if (g != 1) + return 8; + if (x) + return 9; + } + + { + g = 0; + Ptr x, y; + x = Mk(); + y = Mk(); +#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(); + } + if (g != 1) + return 12; + } + + { + g = 0; + { + auto x = Mk(); + 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(); + if (g) + return 14; + x.reset(); + if (g) + return 15; + x = Mk(); + if (g != 1) + return 16; + } +#endif + + // next is 18 + + return 0; +}