#pragma once #include #include #include // NOLINTBEGIN(cppcoreguidelines-macro-usage) #if __cplusplus >= 202002L #define CPP20(...) __VA_ARGS__ #else #define CPP20(...) #endif namespace stdx { inline namespace v1 { // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) template class atomic { static_assert(std::is_trivially_copyable_v and std::is_copy_constructible_v and std::is_move_constructible_v and std::is_copy_assignable_v and std::is_move_assignable_v, "Atomic values must be trivially copyable, copy " "constructible and copy assignable"); using elem_t = ::atomic::atomic_type_t; constexpr static auto alignment = ::atomic::alignment_of; static_assert(std::is_convertible_v, "::atomic::atomic_type_t specialization result must be " "convertible to T"); static_assert(std::is_convertible_v, "::atomic::atomic_type_t specialization result must be " "convertible from T"); alignas(alignment) elem_t value; public: using value_type = T; constexpr atomic() CPP20(requires std::is_default_constructible_v) : value{} {} constexpr explicit atomic(T t) : value{static_cast(t)} {} atomic(atomic const &) = delete; auto operator=(atomic const &) -> atomic & = delete; [[nodiscard]] auto load(std::memory_order mo = std::memory_order_seq_cst) const -> T { return static_cast(::atomic::load(value, mo)); } void store(T t, std::memory_order mo = std::memory_order_seq_cst) { ::atomic::store(value, static_cast(t), mo); } // NOLINTNEXTLINE(google-explicit-constructor) [[nodiscard]] operator T() const { return load(); } // NOLINTNEXTLINE(misc-unconventional-assign-operator) auto operator=(T t) -> T { store(t); return t; } [[nodiscard]] auto exchange(T t, std::memory_order mo = std::memory_order_seq_cst) -> T { return ::atomic::exchange(value, static_cast(t), mo); } auto fetch_add(T t, std::memory_order mo = std::memory_order_seq_cst) -> T { CPP20(static_assert( requires { t + t; }, "T must support operator+(x, y)")); return ::atomic::fetch_add(value, static_cast(t), mo); } auto fetch_sub(T t, std::memory_order mo = std::memory_order_seq_cst) -> T { CPP20(static_assert( requires { t - t; }, "T must support operator-(x, y)")); return ::atomic::fetch_sub(value, static_cast(t), mo); } auto operator+=(T t) -> T { return fetch_add(t) + t; } auto operator-=(T t) -> T { return fetch_sub(t) - t; } auto operator++() -> T { CPP20(static_assert( requires(T t) { ++t; }, "T must support operator++()")); return ::atomic::fetch_add(value, 1) + 1; } [[nodiscard]] auto operator++(int) -> T { CPP20(static_assert( requires(T t) { t++; }, "T must support operator++(int)")); return ::atomic::fetch_add(value, 1); } auto operator--() -> T { CPP20(static_assert( requires(T t) { --t; }, "T must support operator--()")); return ::atomic::fetch_sub(value, 1) - 1; } [[nodiscard]] auto operator--(int) -> T { CPP20(static_assert( requires(T t) { t--; }, "T must support operator--(int)")); return ::atomic::fetch_sub(value, 1); } auto fetch_and(T t, std::memory_order mo = std::memory_order_seq_cst) -> T { CPP20(static_assert( requires { t & t; }, "T must support operator&(x, y)")); return ::atomic::fetch_and(value, static_cast(t), mo); } auto fetch_or(T t, std::memory_order mo = std::memory_order_seq_cst) -> T { CPP20(static_assert( requires { t | t; }, "T must support operator|(x, y)")); return ::atomic::fetch_or(value, static_cast(t), mo); } auto fetch_xor(T t, std::memory_order mo = std::memory_order_seq_cst) -> T { CPP20(static_assert( requires { t ^ t; }, "T must support operator^(x, y)")); return ::atomic::fetch_xor(value, static_cast(t), mo); } auto operator&=(T t) -> T { return fetch_and(t) & t; } auto operator|=(T t) -> T { return fetch_or(t) | t; } auto operator^=(T t) -> T { return fetch_xor(t) ^ t; } }; } // namespace v1 } // namespace stdx // NOLINTEND(cppcoreguidelines-macro-usage)