Skip to content

Commit

Permalink
Clearable stack (#7)
Browse files Browse the repository at this point in the history
* Clearable stack

* format

* add requirement

* requires requires parentheses

* update clang-format

* reformat

* add typename

* add clear test

* QoL additions

---------

Co-authored-by: yaito3014 <[email protected]>
  • Loading branch information
saki7 and yaito3014 authored Aug 20, 2024
1 parent d285da0 commit d677e71
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ BasedOnStyle: Google
AccessModifierOffset: -2
ColumnLimit: 160
IncludeBlocks: Preserve
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
AfterRequiresInClause: true
158 changes: 158 additions & 0 deletions include/yk/stack.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#ifndef YK_STACK_HPP
#define YK_STACK_HPP

#include "yk/allocator/concepts.hpp"

#include <compare>
#include <iterator>
#include <memory>
#include <ranges>
#include <stack>
#include <type_traits>
#include <vector>

namespace yk {

// stack with clear()
// also, defaults to std::vector<T> instead of std::deque<T>
template <class T, class Container = std::vector<T>>
class stack : protected std::stack<T, Container> {
using base_stack = std::stack<T, Container>;

public:
using typename base_stack::const_reference;
using typename base_stack::container_type;
using typename base_stack::reference;
using typename base_stack::size_type;
using typename base_stack::value_type;

using base_stack::base_stack;

using base_stack::empty;
using base_stack::push;
using base_stack::size;
using base_stack::top;

#if __cpp_lib_containers_ranges >= 202202L
using base_stack::push_range;
#endif

using base_stack::emplace;
using base_stack::pop;

constexpr void swap(stack& other) noexcept(std::is_nothrow_swappable_v<Container>) {
using std::swap;
swap(c, other.c);
}

// our addition
constexpr stack(std::initializer_list<T> il) noexcept(noexcept(stack(il.begin(), il.end()))) : stack(il.begin(), il.end()) {}

[[nodiscard]] constexpr size_type capacity() const noexcept
requires requires(Container c) { c.capacity(); }
{
return c.capacity();
}

constexpr void clear() noexcept(noexcept(c.clear()))
requires requires(Container c) { c.clear(); }
{
c.clear();
}

[[nodiscard]] constexpr size_type max_size() const noexcept { return c.max_size(); }

constexpr void reserve(size_type n)
requires requires(Container c) { c.reserve(n); }
{
c.reserve(n);
}

constexpr void shrink_to_fit()
requires requires(Container c) { c.shrink_to_fit(); }
{
c.shrink_to_fit();
}

protected:
using base_stack::c;

public:
[[nodiscard]] friend constexpr bool operator==(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) ==
static_cast<base_stack const&>(rhs))) {
return static_cast<base_stack const&>(lhs) == static_cast<base_stack const&>(rhs);
}

[[nodiscard]] friend constexpr bool operator!=(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) !=
static_cast<base_stack const&>(rhs))) {
return static_cast<base_stack const&>(lhs) != static_cast<base_stack const&>(rhs);
}

[[nodiscard]] friend constexpr bool operator<(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) <
static_cast<base_stack const&>(rhs))) {
return static_cast<base_stack const&>(lhs) < static_cast<base_stack const&>(rhs);
}

[[nodiscard]] friend constexpr bool operator<=(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) <=
static_cast<base_stack const&>(rhs))) {
return static_cast<base_stack const&>(lhs) <= static_cast<base_stack const&>(rhs);
}

[[nodiscard]] friend constexpr bool operator>(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) >
static_cast<base_stack const&>(rhs))) {
return static_cast<base_stack const&>(lhs) > static_cast<base_stack const&>(rhs);
}

[[nodiscard]] friend constexpr bool operator>=(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) >=
static_cast<base_stack const&>(rhs))) {
return static_cast<base_stack const&>(lhs) >= static_cast<base_stack const&>(rhs);
}

[[nodiscard]] friend constexpr auto operator<=>(stack const& lhs, stack const& rhs) noexcept(noexcept(static_cast<base_stack const&>(lhs) <=>
static_cast<base_stack const&>(rhs)))
requires std::three_way_comparable<Container>
{
return static_cast<base_stack const&>(lhs) <=> static_cast<base_stack const&>(rhs);
}
};

template <class Container>
requires (!xo::simple_allocator<Container>)
stack(Container) -> stack<typename Container::value_type, Container>;

template <class Container, class Alloc>
requires (!xo::simple_allocator<Container>) && std::uses_allocator_v<Container, Alloc>
stack(Container, Alloc) -> stack<typename Container::value_type, Container>;

#if __cpp_lib_adaptor_iterator_pair_constructor >= 202106L
// C++23
template <std::input_iterator InputIt, xo::simple_allocator Alloc = std::allocator<std::iter_value_t<InputIt>>>
stack(InputIt, InputIt, Alloc = Alloc()) -> stack<std::iter_value_t<InputIt>, std::vector<std::iter_value_t<InputIt>, Alloc>>;
#endif

#if __cpp_lib_containers_ranges >= 202202L
// C++23
template <std::ranges::input_range R>
stack(std::from_range_t, R&&) -> stack<std::ranges::range_value_t<R>>;

// C++23
template <std::ranges::input_range R, xo::simple_allocator Alloc>
stack(std::from_range_t, R&&, Alloc) -> stack<std::ranges::range_value_t<R>, std::vector<std::ranges::range_value_t<R>, Alloc>>;
#endif

template <class T, class Container>
requires std::is_swappable_v<Container>
void swap(stack<T, Container>& lhs, stack<T, Container>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}

} // namespace yk

namespace std {

template <class T, class Container, class Alloc>
struct uses_allocator<::yk::stack<T, Container>, Alloc> : uses_allocator<Container, Alloc>::type {};

} // namespace std

#endif
15 changes: 15 additions & 0 deletions test/test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "yk/allocator/default_init_allocator.hpp"
#include "yk/stack.hpp"
#include "yk/util/forward_like.hpp"
#include "yk/util/pack_indexing.hpp"
#include "yk/util/reverse.hpp"
Expand Down Expand Up @@ -155,4 +156,18 @@ BOOST_AUTO_TEST_CASE(Allocator) {
}
}

BOOST_AUTO_TEST_CASE(Stack) {
yk::stack<int> s{3, 1, 4, 1, 5};

s.shrink_to_fit();
BOOST_TEST(s.capacity() == 5);
s.reserve(10);
BOOST_TEST(s.capacity() >= 10);
BOOST_TEST(s.capacity() <= s.max_size());

BOOST_TEST(!s.empty());
s.clear();
BOOST_TEST(s.empty());
}

BOOST_AUTO_TEST_SUITE_END() // yk_util

0 comments on commit d677e71

Please sign in to comment.