Just how powerful is GPT-5?
I’ll cut to the chase. Here’s the result of a 40-minute collaboration with it.
The code should be released under GPLv3.0. I can’t conveniently create a GitHub repository for a copy, so please find the license file here: https://www.gnu.org/licenses/
Here are the prompts:
Let's write a Stack class in C++23!
1. Use `template <typename... Types>` with `std::variant` and `std::visit` to solve alignment and type safety issues.
2. Support standard library-style iterators.
3. Use `std::construct_at` and `std::destroy_at` to manually manage object lifetimes and avoid unnecessary overhead.
4. Be compatible with `pmr` and `allocator`, supporting changes in allocation strategy.
5. Use `concept`s to clearly define the relevant requirements and avoid template metaprogramming gibberish.
Excellent work, let's iterate on it now:
1. Check for issues with template parameter pack expansion.
[compiler log ...]
2. Unless necessary, use alternative logical operators like `and` and `or` instead of old C-style ones.
3. Use Doxygen-style comments with detailed explanations in both English and Chinese.
4. For daily use, set appropriate default parameters and enable template parameter deduction wherever possible.
Hold on, let's fix some bugs!
[compiler log ...]
For this update, let's iterate on these points:
1. Introduce finer-grained exception guarantees with `noexcept(move)`.
2. Provide a `small_buffer` optimization (SBO) version.
3. Enhance static reflection where feasible to optimize for fundamental types and small, alignment-friendly types.
Keeping the design requirements the same (i.e., the existing semantic constraints), let's fix the bugs:
[compiler log ...]
Hmm, I think this is great, you're doing an amazing job!
Next task: generate some test code to perform saturation testing on our stack; write it all within the `main` function.
Hmm, there's a problem with your test code. Please fix the bug:
[compiler log ...]
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2025 azureveil <azureveil4295@yeah.net>
//
// This file is part of azureveil stack demo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <variant>
#include <memory>
#include <memory_resource>
#include <type_traits>
#include <concepts>
#include <iterator>
#include <utility>
#include <new>
#include <cstddef>
#include <stdexcept>
#include <algorithm>
#include <initializer_list>
#include <array>
#include <cstring> // memcpy
#include <cassert>
namespace tools
{
/**
* @brief A type-list for meta programming. / 用于元编程的类型列表
*/
template <class... Ts>
struct type_list
{
};
/* ========================= Concepts(概念)========================= */
/**
* @brief Check T is in pack Ts... / 判断类型 T 是否在 Ts... 中
*/
template <class T, class... Ts>
concept InPack = (std::same_as<std::remove_cvref_t<T>, Ts> or ...);
/**
* @brief Element pack requirements for stack. / 栈的候选类型包需求
* - every type is destructible and move-constructible
* - pack size > 0
*
* 重要:折叠表达式必须写成 ((A and B) and ...),避免编译器报
* "binary expression in operand of fold-expression"。
*/
template <class... Ts>
concept StackElementPack =
((std::destructible<Ts> and std::move_constructible<Ts>) and ...) and (sizeof...(Ts) > 0);
/**
* @brief Allocator can be rebound to T. / 分配器可重绑定到 T
*/
template <class Alloc, class T>
concept RebindableAllocator = requires {
typename std::allocator_traits<Alloc>::template rebind_alloc<T>;
};
/**
* @brief f is a valid visitor for std::variant<Ts...>. / 访问器概念
*/
template <class F, class... Ts>
concept VariantVisitor =
requires(F f, std::variant<Ts...> &v) { std::visit(f, v); } and
requires(F f, const std::variant<Ts...> &cv) { std::visit(f, cv); };
/* ========================= meta: unique types ===================== */
namespace detail
{
/**
* @brief contains<List, T> -> bool_constant / 判断 T 是否在 type_list 中
*/
template <class List, class T>
struct contains;
template <class T>
struct contains<type_list<>, T> : std::false_type
{
};
template <class Head, class... Tail, class T>
struct contains<type_list<Head, Tail...>, T>
: std::conditional_t<std::same_as<Head, T>, std::true_type, contains<type_list<Tail...>, T>>
{
};
/**
* @brief prepend<List, T> / 在列表前部插入
*/
template <class List, class T>
struct prepend;
template <class... Ts, class T>
struct prepend<type_list<Ts...>, T>
{
using type = type_list<T, Ts...>;
};
/**
* @brief unique_types<Ts...> 去重并保持首次出现顺序 / make unique, keep first-appearance order
*/
template <class... Ts>
struct unique_types;
template <>
struct unique_types<>
{
using type = type_list<>;
};
template <class T, class... Rest>
struct unique_types<T, Rest...>
{
using rest = typename unique_types<Rest...>::type;
static constexpr bool in = contains<rest, T>::value;
using type = std::conditional_t<in, rest, typename prepend<rest, T>::type>;
};
/**
* @brief forward decl: to_basic_stack<Alloc, type_list<...>>
* 仅声明,不定义,避免重定义冲突!
*/
template <class Alloc, class List>
struct to_basic_stack;
} // namespace detail
/* ========================= basic_stack with SBO =================== */
/**
* @class basic_stack
* @brief A type-safe, allocator-aware multi-type stack based on std::variant,
* with optional Small Buffer Optimization (SBO).
* 使用 std::variant 的类型安全、分配器友好、可选小对象优化(SBO)的多类型栈。
*
* @tparam Allocator allocator type (defaults to std::allocator<std::byte>)
* 分配器类型(默认 std::allocator<std::byte>)
* @tparam SBO inline capacity in elements for small-buffer optimization
* 小对象优化的内联容量(按元素个数)
* @tparam Types... element alternative types (non-empty pack)
* 备选元素类型(非空包)
*
* # Design highlights / 设计要点
* - Storage is `std::variant<Types...>` for type-safety & alignment.(对齐与类型安全由 variant 保证)
* - Manual lifetime with `std::construct_at` / `std::destroy_at`.(手动控制对象生命周期)
* - Standard-style iterators (contiguous).(标准库风格迭代器,连续存储)
* - Supports PMR 和常规分配器。/ Supports PMR and regular allocators.
* - `noexcept(move)` granular policy: relocation chooses move-or-copy by traits. /
* 基于类型性质(traits)选择“移动或拷贝”,提供细粒度异常保证。
* - SBO: small sizes live on-stack; larger spill to heap. / 小容量走栈上内联,大容量溢出到堆。
*
* @note 对“静态反射(static reflection)”的可行优化:
* - 若 `storage_type`(即 `std::variant<Ts...>`)满足
* `std::is_trivially_copyable_v`,则迁移使用 `memcpy` 快路径;
* - 否则若 `std::is_nothrow_move_constructible_v<storage_type>`,则采用移动构造;
* - 否则退化为拷贝构造,保持强异常保证(strong exception safety)。
*/
template <
class Allocator = std::allocator<std::byte>,
std::size_t SBO = 0,
class... Types>
requires StackElementPack<Types...> and RebindableAllocator<Allocator, std::variant<Types...>>
class basic_stack
{
public:
using storage_type = std::variant<Types...>;
using value_type = storage_type;
using allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<storage_type>;
using allocator_traits = std::allocator_traits<allocator_type>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = storage_type &;
using const_reference = const storage_type &;
using pointer = storage_type *;
using const_pointer = const storage_type *;
using iterator = storage_type *;
using const_iterator = const storage_type *;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
private:
// ---- Static reflection knobs / 静态性质开关 ----
static constexpr bool triv_copyable = std::is_trivially_copyable_v<storage_type>;
static constexpr bool triv_destruct = std::is_trivially_destructible_v<storage_type>;
static constexpr bool nothrow_move = std::is_nothrow_move_constructible_v<storage_type>;
static constexpr std::size_t TSize = sizeof(storage_type);
static constexpr std::size_t TAlign = alignof(storage_type);
static constexpr bool has_sbo = (SBO > 0);
/// @brief SBO 内联块(仅在 has_sbo 为真时作为候选)/ inline storage when SBO enabled
struct alignas(TAlign) sbo_block
{
std::array<std::byte, SBO * TSize> buf{};
};
/// @brief 无 SBO 时的占位类型(空壳)/ empty placeholder when SBO disabled
struct no_sbo_block
{
};
/// @brief 依据 has_sbo 选择实际的 sbo_ 成员类型
using sbo_storage_t = std::conditional_t<has_sbo, sbo_block, no_sbo_block>;
public:
/* ------------- constructors / 构造与析构 ------------- */
/**
* @brief Default ctor (uses SBO if available, otherwise no allocation).
* 默认构造(若启用 SBO 则指向内联缓冲,否则不分配)
*/
basic_stack() noexcept(noexcept(allocator_type{}))
: alloc_{}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()} {}
/**
* @brief Construct with allocator. / 指定分配器构造
*/
explicit basic_stack(const Allocator &a)
: alloc_{rebind_alloc_(a)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()} {}
/**
* @brief PMR resource ctor (enabled only for PMR allocator). /
* PMR 资源构造(仅当分配器为 PMR 时启用)
*/
basic_stack(std::pmr::memory_resource *r)
requires std::same_as<std::remove_cvref_t<Allocator>, std::pmr::polymorphic_allocator<std::byte>>
: alloc_{allocator_type{r}}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
{
}
/**
* @brief Reserve capacity upfront. / 预留容量的构造
* @param reserve_cap initial capacity / 初始容量
* @param a allocator (optional) / 可选分配器
*/
explicit basic_stack(size_type reserve_cap, const Allocator &a = Allocator{})
: alloc_{rebind_alloc_(a)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
{
if (reserve_cap > cap_)
reserve(reserve_cap);
}
/**
* @brief Construct from initializer_list of variants. / 从 variant 列表构造
*/
basic_stack(std::initializer_list<storage_type> init, const Allocator &a = Allocator{})
: alloc_{rebind_alloc_(a)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
{
reserve(init.size());
for (auto const &v : init)
push(v);
}
basic_stack(const basic_stack &other)
: alloc_{allocator_traits::select_on_container_copy_construction(other.alloc_)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
{
reserve(other.size_);
try
{
uninit_copy_n_(other.data_, other.size_, data_);
size_ = other.size_;
}
catch (...)
{
destroy_n_(data_, size_);
deallocate_if_heap_();
throw;
}
}
basic_stack(basic_stack &&other) noexcept
: alloc_{std::move(other.alloc_)}, data_{init_data_ptr_()}, size_{0}, cap_{init_cap_()}
{
// 优先:若分配器“总是相等”,可直接窃取缓冲
if constexpr (allocator_traits::is_always_equal::value)
{
if (other.using_heap_())
{
data_ = other.data_;
cap_ = other.cap_;
size_ = other.size_;
other.data_ = other.inline_ptr_();
other.cap_ = other.init_cap_();
other.size_ = 0;
return;
}
}
// PMR 特例:同一 resource 也可安全窃取
else if constexpr (std::same_as<std::remove_cvref_t<Allocator>, std::pmr::polymorphic_allocator<std::byte>>)
{
if (other.using_heap_() and alloc_.resource() == other.alloc_.resource())
{
data_ = other.data_;
cap_ = other.cap_;
size_ = other.size_;
other.data_ = other.inline_ptr_();
other.cap_ = other.init_cap_();
other.size_ = 0;
return;
}
}
}
basic_stack &operator=(const basic_stack &other)
{
if (this == &other)
return *this;
if constexpr (allocator_traits::propagate_on_container_copy_assignment::value)
{
if (alloc_ != other.alloc_)
{
clear();
deallocate_if_heap_();
}
alloc_ = other.alloc_;
}
if (other.size_ > cap_)
{
reallocate_to_(other.size_);
}
// 赋值已有元素并填充/销毁多余
const size_type common = std::min(size_, other.size_);
for (size_type i = 0; i < common; ++i)
data_[i] = other.data_[i];
if (size_ < other.size_)
{
uninit_copy_n_(other.data_ + size_, other.size_ - size_, data_ + size_);
}
else
{
destroy_n_(data_ + other.size_, size_ - other.size_);
}
size_ = other.size_;
return *this;
}
basic_stack &operator=(basic_stack &&other) noexcept(
allocator_traits::propagate_on_container_move_assignment::value or
std::is_nothrow_move_assignable_v<allocator_type>)
{
if (this == &other)
return *this;
if constexpr (allocator_traits::propagate_on_container_move_assignment::value)
{
clear();
deallocate_if_heap_();
alloc_ = std::move(other.alloc_);
if (other.using_heap_())
{
data_ = other.data_;
size_ = other.size_;
cap_ = other.cap_;
other.data_ = other.inline_ptr_();
other.size_ = 0;
other.cap_ = other.init_cap_();
}
else
{
data_ = inline_ptr_();
cap_ = init_cap_();
size_ = 0;
reserve(other.size_);
uninit_move_or_copy_n_(other.data_, other.size_, data_);
size_ = other.size_;
other.clear();
}
}
else
{
if (alloc_ == other.alloc_)
{
clear();
deallocate_if_heap_();
if (other.using_heap_())
{
data_ = other.data_;
size_ = other.size_;
cap_ = other.cap_;
other.data_ = other.inline_ptr_();
other.size_ = 0;
other.cap_ = other.init_cap_();
}
else
{
data_ = inline_ptr_();
cap_ = init_cap_();
size_ = 0;
reserve(other.size_);
uninit_move_or_copy_n_(other.data_, other.size_, data_);
size_ = other.size_;
other.clear();
}
}
else
{
assign_move_from_(other);
}
}
return *this;
}
~basic_stack()
{
destroy_n_(data_, size_);
deallocate_if_heap_();
}
/* ------------- iterators / 迭代器 ------------- */
/// @brief begin / 起始迭代器
iterator begin() noexcept { return data_; }
const_iterator begin() const noexcept { return data_; }
const_iterator cbegin() const noexcept { return data_; }
/// @brief end / 末尾迭代器
iterator end() noexcept { return data_ + size_; }
const_iterator end() const noexcept { return data_ + size_; }
const_iterator cend() const noexcept { return data_ + size_; }
/// @brief reverse begin / 反向起始
reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{end()}; }
const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{end()}; }
/// @brief reverse end / 反向末尾
reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator{begin()}; }
const_reverse_iterator crend() const noexcept { return const_reverse_iterator{begin()}; }
/* ------------- capacity / 容量 ------------- */
/// @brief empty? / 是否为空
[[nodiscard]] bool empty() const noexcept { return size_ == 0; }
/// @brief size / 当前大小
[[nodiscard]] size_type size() const noexcept { return size_; }
/// @brief capacity / 当前容量
[[nodiscard]] size_type capacity() const noexcept { return cap_; }
/// @brief reserve capacity / 预留容量
void reserve(size_type new_cap)
{
if (new_cap <= cap_)
return;
reallocate_to_(new_cap);
}
/// @brief shrink to fit (may move back into SBO) / 收缩容量(可能回迁至 SBO)
void shrink_to_fit()
{
if (size_ == cap_)
return;
const size_type target = size_;
// 若启用 SBO 且可容纳,则回迁内联
if constexpr (has_sbo)
{
if (target <= SBO)
{
reallocate_to_(SBO);
return;
}
}
reallocate_to_(target);
}
/// @brief clear (destroy all elements) / 清空(析构所有元素)
void clear() noexcept
{
destroy_n_(data_, size_);
size_ = 0;
}
/* ------------- element access / 访问 ------------- */
/// @brief top (mutable) / 顶部元素(可变)
reference top()
{
if (empty())
throw std::out_of_range("stack is empty");
return data_[size_ - 1];
}
/// @brief top (const) / 顶部元素(常量)
const_reference top() const
{
if (empty())
throw std::out_of_range("stack is empty");
return data_[size_ - 1];
}
/**
* @brief Visit top via visitor. / 使用访问器访问顶部元素
*/
template <VariantVisitor<Types...> F>
decltype(auto) visit_top(F &&f)
{
return std::visit(std::forward<F>(f), top());
}
template <VariantVisitor<Types...> F>
decltype(auto) visit_top(F &&f) const
{
return std::visit(std::forward<F>(f), top());
}
/**
* @brief Visit all elements. / 访问所有元素
*/
template <VariantVisitor<Types...> F>
void visit_all(F &&f)
{
for (auto &e : *this)
std::visit(f, e);
}
template <VariantVisitor<Types...> F>
void visit_all(F &&f) const
{
for (auto &e : *this)
std::visit(f, e);
}
/* ------------- modifiers / 修改器 ------------- */
/**
* @brief Push a variant value. / 压入一个 variant 值
*/
void push(value_type v)
{
ensure_growth_();
std::construct_at(data_ + size_, std::move(v));
++size_;
}
/**
* @brief Push a concrete alternative in Types... / 压入具体候选类型的值
*/
template <class T>
requires InPack<T, Types...>
void push(T &&x)
{
ensure_growth_();
std::construct_at(
data_ + size_,
std::in_place_type<std::remove_cvref_t<T>>,
std::forward<T>(x));
++size_;
}
/**
* @brief Emplace the top element as alternative T. / 原地构造指定候选类型 T 的顶部元素
*/
template <class T, class... Args>
requires InPack<T, Types...> and std::constructible_from<T, Args...>
reference emplace(Args &&...args)
{
ensure_growth_();
std::construct_at(data_ + size_, std::in_place_type<T>, std::forward<Args>(args)...);
++size_;
return data_[size_ - 1];
}
/**
* @brief Pop the top element (destroy). / 弹出顶部元素(析构)
*/
void pop()
{
if (empty())
throw std::out_of_range("stack is empty");
std::destroy_at(data_ + (size_ - 1));
--size_;
}
/* ------------- allocator / 分配器 ------------- */
/// @brief get allocator / 获取分配器
allocator_type get_allocator() const noexcept { return alloc_; }
/**
* @brief Rebind to another allocator type. / 迁移到另一种分配器策略
* @note Copy/move variant elements as needed. / 根据异常安全选择拷贝或移动
*/
template <class NewAlloc>
requires RebindableAllocator<NewAlloc, storage_type>
basic_stack<NewAlloc, SBO, Types...> rebind_allocator(const NewAlloc &na = NewAlloc{}) const
{
basic_stack<NewAlloc, SBO, Types...> dst(na);
dst.reserve(size_);
for (size_type i = 0; i < size_; ++i)
{
dst.push(data_[i]);
}
return dst;
}
private:
/* ------------- internal helpers / 内部工具 ------------- */
// —— SBO helpers / SBO 辅助 —— //
pointer inline_ptr_() noexcept
{
if constexpr (has_sbo)
{
// std::launder: 避免潜在的严格别名与生命周期问题
return std::launder(reinterpret_cast<pointer>(sbo_.buf.data()));
}
else
{
return nullptr;
}
}
const_pointer inline_ptr_() const noexcept
{
if constexpr (has_sbo)
{
return std::launder(reinterpret_cast<const_pointer>(sbo_.buf.data()));
}
else
{
return nullptr;
}
}
bool using_heap_() const noexcept
{
if constexpr (has_sbo)
return data_ != inline_ptr_();
else
return data_ != nullptr;
}
pointer init_data_ptr_() noexcept
{
if constexpr (has_sbo)
return inline_ptr_();
else
return nullptr;
}
size_type init_cap_() const noexcept
{
if constexpr (has_sbo)
return SBO;
else
return 0;
}
static allocator_type rebind_alloc_(const Allocator &a)
{
using rebound = typename std::allocator_traits<Allocator>::template rebind_alloc<storage_type>;
return rebound{a};
}
pointer allocate_(size_type n) { return allocator_traits::allocate(alloc_, n); }
void deallocate_if_heap_() noexcept
{
if (using_heap_() and data_)
allocator_traits::deallocate(alloc_, data_, cap_);
data_ = init_data_ptr_();
cap_ = init_cap_();
}
static void destroy_n_(pointer p, size_type n) noexcept
{
for (size_type i = 0; i < n; ++i)
{
std::destroy_at(p + i);
}
}
static void uninit_copy_n_(const_pointer src, size_type n, pointer dst)
{
size_type i = 0;
try
{
for (; i < n; ++i)
std::construct_at(dst + i, src[i]);
}
catch (...)
{
destroy_n_(dst, i);
throw;
}
}
// —— granular move/copy by traits / 基于性质的精细迁移 —— //
static void uninit_move_or_copy_n_(pointer src, size_type n, pointer dst)
{
// Fast path: trivially copyable → memcpy
if constexpr (triv_copyable)
{
std::memcpy(static_cast<void *>(dst), static_cast<void const *>(src), n * TSize);
return;
}
size_type i = 0;
try
{
if constexpr (nothrow_move)
{
for (; i < n; ++i)
std::construct_at(dst + i, std::move(src[i]));
}
else
{
for (; i < n; ++i)
std::construct_at(dst + i, src[i]); // fallback copy
}
}
catch (...)
{
destroy_n_(dst, i);
throw;
}
}
void reallocate_to_(size_type new_cap)
{
// 选择目标缓冲区:优先使用 SBO(若可容纳)
pointer new_buf = nullptr;
if constexpr (has_sbo)
{
new_buf = (new_cap <= SBO) ? inline_ptr_() : allocate_(new_cap);
}
else
{
new_buf = allocate_(new_cap);
}
if (new_buf == data_)
{ // 仍在同一缓冲,无需动作
cap_ = (new_buf == inline_ptr_()) ? init_cap_() : new_cap;
return;
}
size_type moved = 0;
try
{
uninit_move_or_copy_n_(data_, size_, new_buf);
moved = size_;
}
catch (...)
{
if (new_buf != inline_ptr_())
{
destroy_n_(new_buf, moved); // 对于 memcpy 情况,trivial dtor 则无副作用
allocator_traits::deallocate(alloc_, new_buf, new_cap);
}
throw;
}
// 旧缓冲区清理
destroy_n_(data_, size_);
if (using_heap_())
allocator_traits::deallocate(alloc_, data_, cap_);
data_ = new_buf;
cap_ = (data_ == inline_ptr_()) ? init_cap_() : new_cap;
}
void ensure_growth_()
{
if (size_ == cap_)
{
const size_type base = (cap_ == 0) ? (has_sbo ? SBO : 0) : cap_;
const size_type new_cap = (base == 0) ? 4 : (base * 2);
reallocate_to_(new_cap);
}
}
void assign_move_from_(basic_stack &other)
{
clear();
reserve(other.size_);
uninit_move_or_copy_n_(other.data_, other.size_, data_);
size_ = other.size_;
other.clear();
}
private:
allocator_type alloc_{};
pointer data_{nullptr};
size_type size_{0};
size_type cap_{0};
// SBO storage (exists only if SBO>0) / 仅当 SBO>0 才有意义
sbo_storage_t sbo_{};
};
/* ========================= Aliases / 别名 ========================= */
template <class... Types>
using Stack = basic_stack<std::allocator<std::byte>, 0, Types...>;
template <std::size_t SBO, class... Types>
using SmallStack = basic_stack<std::allocator<std::byte>, SBO, Types...>;
template <class... Types>
using PmrStack = basic_stack<std::pmr::polymorphic_allocator<std::byte>, 0, Types...>;
template <std::size_t SBO, class... Types>
using PmrSmallStack = basic_stack<std::pmr::polymorphic_allocator<std::byte>, SBO, Types...>;
/* === meta glue: materialize to_basic_stack (after basic_stack exists) === */
/* !!!修复重定义:只在此处做唯一的偏特化定义,之前仅声明不定义。 */
namespace detail
{
template <class Alloc, class... Ts>
struct to_basic_stack<Alloc, type_list<Ts...>>
{
using type = basic_stack<Alloc, 0, Ts...>;
};
} // namespace detail
/* ========================= Factory helpers / 工厂辅助 ==================== */
/**
* @brief Make a stack with deduced unique types from arguments (default allocator).
* 通过实参类型推导并去重,构造使用默认分配器的栈。
*
* @return stack with Types... = unique(remove_cvref_t<Args>...) / 去重后的类型包
*
* 使用示例:
* auto s = tools::stack_of(1, std::string{"x"}, 3.14); // Stack<int, std::string, double>
*/
template <class... Args>
auto stack_of(Args &&...)
{
using uniq_list = typename detail::unique_types<std::remove_cvref_t<Args>...>::type;
using stack_t = typename detail::to_basic_stack<std::allocator<std::byte>, uniq_list>::type;
return stack_t{};
}
/**
* @brief Make a stack with a given allocator. / 使用给定分配器的工厂
*/
template <class Alloc, class... Args>
requires RebindableAllocator<Alloc, std::variant<std::remove_cvref_t<Args>...>>
auto stack_of_with_alloc(const Alloc &a, Args &&...)
{
using uniq_list = typename detail::unique_types<std::remove_cvref_t<Args>...>::type;
using stack_t = typename detail::to_basic_stack<Alloc, uniq_list>::type;
return stack_t{a};
}
/**
* @brief PMR convenience factory. / PMR 快捷工厂
*/
template <class... Args>
auto stack_of_pmr(std::pmr::memory_resource *r, Args &&...)
{
using uniq_list = typename detail::unique_types<std::remove_cvref_t<Args>...>::type;
using stack_t = typename detail::to_basic_stack<std::pmr::polymorphic_allocator<std::byte>, uniq_list>::type;
return stack_t{r};
}
/* ========================= CTAD guides / 推导指引 ======================== */
/**
* @brief Deduce Ts... from initializer_list<variant<Ts...>> (default allocator).
* 从 initializer_list<variant<Ts...>> 推导元素类型(默认分配器)
*/
template <class... Ts>
basic_stack(std::initializer_list<std::variant<Ts...>>, std::allocator<std::byte>)
-> basic_stack<std::allocator<std::byte>, 0, Ts...>;
template <class... Ts>
basic_stack(std::initializer_list<std::variant<Ts...>>)
-> basic_stack<std::allocator<std::byte>, 0, Ts...>;
/**
* @brief Deduce Ts... + PMR allocator from initializer_list. / 从初始化列表推导 Ts... 与 PMR 分配器
*/
template <class... Ts>
basic_stack(std::initializer_list<std::variant<Ts...>>, std::pmr::polymorphic_allocator<std::byte>)
-> basic_stack<std::pmr::polymorphic_allocator<std::byte>, 0, Ts...>;
} // namespace tools
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2025 azureveil <azureveil4295@yeah.net>
//
// This file is part of azureveil stack demo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "stack.hpp"
#include <cassert>
#include <string>
#include <iostream>
#include <variant>
#include <vector>
#include <array>
#include <memory_resource>
#include <algorithm>
#include <stdexcept>
#include <cctype>
#include <type_traits>
#include <climits>
// —— 两个测试类型必须放在函数外:标准禁止局部类含静态数据成员 ——
// 计数型:用于验证生命周期(构造/析构数量)
struct Counted
{
static int live;
int id = -1;
Counted() : id(-1) { ++live; }
explicit Counted(int i) : id(i) { ++live; }
Counted(const Counted &o) : id(o.id) { ++live; }
Counted(Counted &&o) noexcept : id(o.id) { ++live; }
Counted &operator=(const Counted &) = default;
Counted &operator=(Counted &&) = default;
~Counted() { --live; }
};
int Counted::live = 0;
// 用于异常路径:拷贝到第 N 次时抛异常;移动构造非 noexcept,促使“回退拷贝路径”
struct ThrowOnCopy
{
static int copy_count;
static int throw_after;
int v = 0;
ThrowOnCopy() = default;
explicit ThrowOnCopy(int x) : v(x) {}
ThrowOnCopy(const ThrowOnCopy &o) : v(o.v)
{
if (++copy_count == throw_after)
throw std::runtime_error("copy boom");
}
ThrowOnCopy(ThrowOnCopy &&o) noexcept(false) : v(o.v) {}
ThrowOnCopy &operator=(const ThrowOnCopy &) = default;
ThrowOnCopy &operator=(ThrowOnCopy &&) = default;
~ThrowOnCopy() = default;
};
int ThrowOnCopy::copy_count = 0;
int ThrowOnCopy::throw_after = INT_MAX;
int main()
{
using namespace tools;
// ========== 1) 基础功能:SmallStack + 多类型 push/emplace/visit ==========
{
SmallStack<4, int, std::string, double, Counted> s; // SBO=4
assert(s.empty());
s.emplace<int>(42);
s.push(std::string{"alpha"});
s.push(3.14);
s.emplace<Counted>(7);
assert(s.size() == 4);
assert(s.capacity() >= 4);
s.visit_top([](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, Counted>) assert(x.id == 7); });
int cnt_int = 0;
int cnt_str = 0;
int cnt_dbl = 0;
int cnt_cnt = 0;
long long sum_int = 0;
double sum_dbl = 0.0;
s.visit_all([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, int>) { ++cnt_int; sum_int += x; }
else if constexpr (std::is_same_v<T, std::string>) { ++cnt_str; }
else if constexpr (std::is_same_v<T, double>) { ++cnt_dbl; sum_dbl += x; }
else if constexpr (std::is_same_v<T, Counted>) { ++cnt_cnt; } });
assert(cnt_int == 1 and cnt_str == 1 and cnt_dbl == 1 and cnt_cnt == 1);
assert(sum_int == 42);
assert(sum_dbl > 3.0 and sum_dbl < 3.2);
int live_before = Counted::live;
s.pop(); // 弹出 Counted{7}
assert(Counted::live == live_before - 1);
assert(s.size() == 3);
s.clear();
assert(s.empty());
}
// ========== 2) 容量行为:SBO 命中、溢出堆、shrink 回迁 ==========
{
SmallStack<4, int, std::string> s;
for (int i = 0; i < 4; ++i)
s.push(i); // SBO 内
assert(s.capacity() == 4);
s.push(100); // 触发溢出
assert(s.capacity() >= 5);
while (s.size() > 4)
s.pop(); // 回到 SBO 大小
s.shrink_to_fit();
assert(s.capacity() == 4); // 回迁成功
}
// ========== 3) reserve/growth:多次重分配不丢数据 ==========
{
Stack<int, std::string> s;
for (int i = 0; i < 100; ++i)
{
if (i % 10 == 0)
s.push(std::string{"s-" + std::to_string(i)});
else
s.push(i);
}
int ints = 0, strs = 0, sum = 0;
s.visit_all([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, int>) { ++ints; sum += x; }
else if constexpr (std::is_same_v<T, std::string>) { ++strs; } });
assert(ints == 90 and strs == 10);
assert(s.size() == 100);
const auto sz = s.size();
s.shrink_to_fit();
assert(s.size() == sz);
}
// ========== 4) PMR:单调缓冲资源 + PmrSmallStack ==========
{
std::array<std::byte, 4096> buf{};
std::pmr::monotonic_buffer_resource mbr(buf.data(), buf.size());
PmrSmallStack<4, int, std::string> ps(&mbr);
ps.emplace<int>(1);
ps.emplace<std::string>("pmr");
ps.emplace<int>(2);
ps.emplace<int>(3); // 填满 SBO
ps.emplace<int>(4); // 溢出到堆(仍由 mbr 管)
assert(ps.size() == 5);
int sum = 0, strings = 0;
ps.visit_all([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, int>) sum += x;
else if constexpr (std::is_same_v<T, std::string>) ++strings; });
assert(sum == (1 + 2 + 3 + 4) and strings == 1);
}
// ========== 5) 工厂函数与 CTAD(不再用严格顺序的 static_assert) ==========
{
auto s1 = stack_of(1, std::string{"x"}, 2.5, 3, 4.0);
using stor1 = typename decltype(s1)::storage_type;
static_assert(std::variant_size_v<stor1> == 3);
// 运行期验证三种类型都能正常压入/访问
s1.push(123);
s1.push(std::string{"ok"});
s1.push(3.14);
bool saw_int = false, saw_str = false, saw_dbl = false;
s1.visit_all([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T,int>) saw_int = true;
if constexpr (std::is_same_v<T,std::string>) saw_str = true;
if constexpr (std::is_same_v<T,double>) saw_dbl = true; });
assert(saw_int and saw_str and saw_dbl);
// PMR 工厂 & initializer_list + CTAD
std::pmr::monotonic_buffer_resource mbr;
auto s2 = stack_of_pmr(&mbr, 1, std::string{"y"}, 3.0);
using stor2 = typename decltype(s2)::storage_type;
static_assert(std::variant_size_v<stor2> == 3);
using V = std::variant<int, std::string>;
basic_stack lst = {V{1}, V{std::string{"ctad"}}}; // Ts... 推导为 <int, std::string>
assert(lst.size() == 2);
}
// ========== 6) rebind_allocator:迁移到 PMR ==========
{
auto s = stack_of(1, std::string{"reb"}, 2.0);
s.push(7);
s.push(std::string{"z"});
std::array<std::byte, 2048> buf{};
std::pmr::monotonic_buffer_resource mbr(buf.data(), buf.size());
auto sp = s.rebind_allocator(std::pmr::polymorphic_allocator<std::byte>{&mbr});
assert(sp.size() == s.size());
auto stringify = [](auto const &v) -> std::string
{
return std::visit([](auto const &x) -> std::string
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, std::string>) return x;
else return std::to_string(x); }, v);
};
std::vector<std::string> a, b;
a.reserve(s.size());
b.reserve(sp.size());
for (auto const &e : s)
a.push_back(stringify(e));
for (auto const &e : sp)
b.push_back(stringify(e));
assert(a == b);
}
// ========== 7) 变更访问:就地变换字符串 ==========
{
SmallStack<4, int, std::string> s;
s.push(1);
s.push(std::string{"MiXed"});
s.push(2);
for (auto &v : s)
{
std::visit([](auto &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, std::string>) {
for (auto& c : x) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
} }, v);
}
bool seen_upper = false;
s.visit_all([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, std::string>) seen_upper = (x == "MIXED"); });
assert(seen_upper);
}
// ========== 8) 异常安全(强保证):拷贝途中抛异常,栈保持不变 ==========
{
using S = Stack<ThrowOnCopy, int>;
S s;
for (int i = 0; i < 8; ++i)
{
if (i % 2 == 0)
s.push(ThrowOnCopy{i});
else
s.push(i);
}
const std::vector<int> snapshot = [&]
{
std::vector<int> vv;
vv.reserve(s.size());
for (auto const &e : s)
{
std::visit([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, int>) vv.push_back(x);
else vv.push_back(x.v); }, e);
}
return vv;
}();
ThrowOnCopy::copy_count = 0;
ThrowOnCopy::throw_after = 3;
const auto old_size = s.size();
const auto old_cap = s.capacity();
bool threw = false;
try
{
s.reserve(old_cap + 8); // 触发重分配 → 拷贝途中抛异常
}
catch (std::runtime_error const &)
{
threw = true;
}
ThrowOnCopy::throw_after = INT_MAX;
assert(threw);
assert(s.size() == old_size); // 强保证:大小不变
std::vector<int> after;
after.reserve(s.size());
for (auto const &e : s)
{
std::visit([&](auto const &x)
{
using T = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<T, int>) after.push_back(x);
else after.push_back(x.v); }, e);
}
assert(after == snapshot);
}
// ========== 9) 生命周期泄漏检查 ==========
{
int before = Counted::live;
{
SmallStack<4, Counted> s;
for (int i = 0; i < 16; ++i)
s.emplace<Counted>(i);
assert(Counted::live >= before + 16);
s.clear();
assert(s.empty());
}
assert(Counted::live == before);
}
// ========== 10) 顶部访问与异常边界 ==========
{
Stack<int> s;
bool threw = false;
try
{
(void)s.top();
}
catch (std::out_of_range const &)
{
threw = true;
}
assert(threw);
s.push(7);
assert(std::get<int>(s.top()) == 7);
s.pop();
assert(s.empty());
}
std::cout << "[OK] all saturated tests passed.\n";
return 0;
}