Mega-cleanup for atomic_t<> and named bit-sets bs_t<>

Remove "atomic operator" classes
Remove test, test_and_set, test_and_reset, test_and_complement global functions
Simplify atomic_t<> with constexpr if, remove some garbage
Redesign bs_t<> to use class, mark its methods constexpr
Implement atomic_bs_t<> for optimizations
Remove unused __bitwise_ops concept (should be in other header anyway)
Bitsets can now be tested via safe bool conversion
This commit is contained in:
Nekotekina 2018-09-02 20:22:35 +03:00
parent a6d06b2e20
commit 8abe6489ed
23 changed files with 604 additions and 1090 deletions

View file

@ -562,206 +562,11 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
// TODO
};
template<typename T1, typename T2, typename = void>
struct atomic_add
{
auto operator()(T1& lhs, const T2& rhs) const
{
return lhs += rhs;
}
};
template<typename T1, typename T2>
struct atomic_add<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::fetch_add;
static constexpr auto op_fetch = &atomic_storage<T1>::add_fetch;
static constexpr auto atomic_op = &atomic_storage<T1>::add_fetch;
};
template<typename T1, typename T2, typename = void>
struct atomic_sub
{
auto operator()(T1& lhs, const T2& rhs) const
{
return lhs -= rhs;
}
};
template<typename T1, typename T2>
struct atomic_sub<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::fetch_sub;
static constexpr auto op_fetch = &atomic_storage<T1>::sub_fetch;
static constexpr auto atomic_op = &atomic_storage<T1>::sub_fetch;
};
template<typename T, typename = void>
struct atomic_pre_inc
{
auto operator()(T& v) const
{
return ++v;
}
};
template<typename T>
struct atomic_pre_inc<T, std::enable_if_t<std::is_integral<T>::value>>
{
static constexpr auto atomic_op = &atomic_storage<T>::inc_fetch;
};
template<typename T, typename = void>
struct atomic_post_inc
{
auto operator()(T& v) const
{
return v++;
}
};
template<typename T>
struct atomic_post_inc<T, std::enable_if_t<std::is_integral<T>::value>>
{
static constexpr auto atomic_op = &atomic_storage<T>::fetch_inc;
};
template<typename T, typename = void>
struct atomic_pre_dec
{
auto operator()(T& v) const
{
return --v;
}
};
template<typename T>
struct atomic_pre_dec<T, std::enable_if_t<std::is_integral<T>::value>>
{
static constexpr auto atomic_op = &atomic_storage<T>::dec_fetch;
};
template<typename T, typename = void>
struct atomic_post_dec
{
auto operator()(T& v) const
{
return v--;
}
};
template<typename T>
struct atomic_post_dec<T, std::enable_if_t<std::is_integral<T>::value>>
{
static constexpr auto atomic_op = &atomic_storage<T>::fetch_dec;
};
template<typename T1, typename T2, typename = void>
struct atomic_and
{
auto operator()(T1& lhs, const T2& rhs) const
{
return lhs &= rhs;
}
};
template<typename T1, typename T2>
struct atomic_and<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::fetch_and;
static constexpr auto op_fetch = &atomic_storage<T1>::and_fetch;
static constexpr auto atomic_op = &atomic_storage<T1>::and_fetch;
};
template<typename T1, typename T2, typename = void>
struct atomic_or
{
auto operator()(T1& lhs, const T2& rhs) const
{
return lhs |= rhs;
}
};
template<typename T1, typename T2>
struct atomic_or<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::fetch_or;
static constexpr auto op_fetch = &atomic_storage<T1>::or_fetch;
static constexpr auto atomic_op = &atomic_storage<T1>::or_fetch;
};
template<typename T1, typename T2, typename = void>
struct atomic_xor
{
auto operator()(T1& lhs, const T2& rhs) const
{
return lhs ^= rhs;
}
};
template<typename T1, typename T2>
struct atomic_xor<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::fetch_xor;
static constexpr auto op_fetch = &atomic_storage<T1>::xor_fetch;
static constexpr auto atomic_op = &atomic_storage<T1>::xor_fetch;
};
template<typename T1, typename T2, typename = void>
struct atomic_test_and_set
{
bool operator()(T1& lhs, const T2& rhs) const
{
return test_and_set(lhs, rhs);
}
};
template<typename T1, typename T2>
struct atomic_test_and_set<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_set;
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_set;
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_set;
};
template<typename T1, typename T2, typename = void>
struct atomic_test_and_reset
{
bool operator()(T1& lhs, const T2& rhs) const
{
return test_and_reset(lhs, rhs);
}
};
template<typename T1, typename T2>
struct atomic_test_and_reset<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_reset;
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_reset;
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_reset;
};
template<typename T1, typename T2, typename = void>
struct atomic_test_and_complement
{
bool operator()(T1& lhs, const T2& rhs) const
{
return test_and_complement(lhs, rhs);
}
};
template<typename T1, typename T2>
struct atomic_test_and_complement<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_complement;
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_complement;
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_complement;
};
// Atomic type with lock-free and standard layout guarantees (and appropriate limitations)
template<typename T>
template <typename T>
class atomic_t
{
protected:
using type = typename std::remove_cv<T>::type;
static_assert(alignof(type) == sizeof(type), "atomic_t<> error: unexpected alignment, use alignas() if necessary");
@ -790,7 +595,7 @@ public:
}
// Atomically compare data with cmp, replace with exch if equal, return previous data value anyway
simple_type compare_and_swap(const type& cmp, const type& exch)
type compare_and_swap(const type& cmp, const type& exch)
{
type old = cmp;
atomic_storage<type>::compare_exchange(m_data, old, exch);
@ -804,83 +609,69 @@ public:
return atomic_storage<type>::compare_exchange(m_data, old, exch);
}
// Atomic operation; returns old value, discards function result value
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, const Args&...)>>
type fetch_op(F&& func, const Args&... args)
// Atomic operation; returns old value
template <typename F>
std::enable_if_t<std::is_void<std::invoke_result_t<F, T&>>::value, type> fetch_op(F&& func)
{
type _new, old = atomic_storage<type>::load(m_data);
while (true)
{
func((_new = old), args...);
func((_new = old));
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) return old;
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) [[likely]]
{
return old;
}
}
}
// Helper overload for calling optimized implementation
template<typename F, typename... Args, typename FT = decltype(F::fetch_op), typename RT = std::result_of_t<FT(T&, const Args&...)>>
type fetch_op(F&&, const Args&... args)
{
return F::fetch_op(m_data, args...);
}
// Atomic operation; returns new value, discards function result value
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, const Args&...)>>
type op_fetch(F&& func, const Args&... args)
// Atomic operation; returns new value
template <typename F>
std::enable_if_t<std::is_void<std::invoke_result_t<F, T&>>::value, type> op_fetch(F&& func)
{
type _new, old = atomic_storage<type>::load(m_data);
while (true)
{
func((_new = old), args...);
func((_new = old));
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) return _new;
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) [[likely]]
{
return _new;
}
}
}
// Helper overload for calling optimized implementation
template<typename F, typename... Args, typename FT = decltype(F::op_fetch), typename RT = std::result_of_t<FT(T&, const Args&...)>>
type op_fetch(F&&, const Args&... args)
{
return F::op_fetch(m_data, args...);
}
// Atomic operation; returns function result value
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, const Args&...)>, typename = std::enable_if_t<!std::is_void<RT>::value>>
// Atomic operation; returns function result value (TODO: remove args)
template <typename F, typename... Args, typename RT = std::invoke_result_t<F, T&, const Args&...>>
RT atomic_op(F&& func, const Args&... args)
{
type _new, old = atomic_storage<type>::load(m_data);
while (true)
{
RT&& result = func((_new = old), args...);
if constexpr(std::is_void<RT>::value)
{
func((_new = old), args...);
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) return std::move(result);
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) [[likely]]
{
return;
}
}
else
{
RT result = func((_new = old), args...);
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) [[likely]]
{
return result;
}
}
}
}
// Overload for void return type
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, const Args&...)>, typename = std::enable_if_t<std::is_void<RT>::value>>
void atomic_op(F&& func, const Args&... args)
{
type _new, old = atomic_storage<type>::load(m_data);
while (true)
{
func((_new = old), args...);
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) return;
}
}
// Helper overload for calling optimized implementation
template<typename F, typename... Args, typename FT = decltype(F::atomic_op), typename RT = std::result_of_t<FT(T&, const Args&...)>>
auto atomic_op(F&&, const Args&... args)
{
return F::atomic_op(m_data, args...);
}
// Atomically read data
type load() const
{
@ -911,144 +702,250 @@ public:
return atomic_storage<type>::exchange(m_data, rhs);
}
template<typename T2>
type fetch_add(const T2& rhs)
type fetch_add(const type& rhs)
{
return fetch_op(atomic_add<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_add(m_data, rhs);
}
return fetch_op([&](T& v)
{
v += rhs;
});
}
template<typename T2>
type add_fetch(const T2& rhs)
type add_fetch(const type& rhs)
{
return op_fetch(atomic_add<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::add_fetch(m_data, rhs);
}
return op_fetch([&](T& v)
{
v += rhs;
});
}
template<typename T2>
auto operator +=(const T2& rhs)
auto operator +=(const type& rhs)
{
return atomic_op(atomic_add<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::add_fetch(m_data, rhs);
}
return atomic_op([&](T& v)
{
return v += rhs;
});
}
template<typename T2>
type fetch_sub(const T2& rhs)
type fetch_sub(const type& rhs)
{
return fetch_op(atomic_sub<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_sub(m_data, rhs);
}
return fetch_op([&](T& v)
{
v -= rhs;
});
}
template<typename T2>
type sub_fetch(const T2& rhs)
type sub_fetch(const type& rhs)
{
return op_fetch(atomic_sub<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::sub_fetch(m_data, rhs);
}
return op_fetch([&](T& v)
{
v -= rhs;
});
}
template<typename T2>
auto operator -=(const T2& rhs)
auto operator -=(const type& rhs)
{
return atomic_op(atomic_sub<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::sub_fetch(m_data, rhs);
}
return atomic_op([&](T& v)
{
return v -= rhs;
});
}
template<typename T2>
type fetch_and(const T2& rhs)
type fetch_and(const type& rhs)
{
return fetch_op(atomic_and<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_and(m_data, rhs);
}
return fetch_op([&](T& v)
{
v &= rhs;
});
}
template<typename T2>
type and_fetch(const T2& rhs)
type and_fetch(const type& rhs)
{
return op_fetch(atomic_and<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::and_fetch(m_data, rhs);
}
return op_fetch([&](T& v)
{
v &= rhs;
});
}
template<typename T2>
auto operator &=(const T2& rhs)
auto operator &=(const type& rhs)
{
return atomic_op(atomic_and<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::and_fetch(m_data, rhs);
}
return atomic_op([&](T& v)
{
return v &= rhs;
});
}
template<typename T2>
type fetch_or(const T2& rhs)
type fetch_or(const type& rhs)
{
return fetch_op(atomic_or<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_or(m_data, rhs);
}
return fetch_op([&](T& v)
{
v |= rhs;
});
}
template<typename T2>
type or_fetch(const T2& rhs)
type or_fetch(const type& rhs)
{
return op_fetch(atomic_or<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::or_fetch(m_data, rhs);
}
return op_fetch([&](T& v)
{
v |= rhs;
});
}
template<typename T2>
auto operator |=(const T2& rhs)
auto operator |=(const type& rhs)
{
return atomic_op(atomic_or<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::or_fetch(m_data, rhs);
}
return atomic_op([&](T& v)
{
return v |= rhs;
});
}
template<typename T2>
type fetch_xor(const T2& rhs)
type fetch_xor(const type& rhs)
{
return fetch_op(atomic_xor<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_xor(m_data, rhs);
}
return fetch_op([&](T& v)
{
v ^= rhs;
});
}
template<typename T2>
type xor_fetch(const T2& rhs)
type xor_fetch(const type& rhs)
{
return op_fetch(atomic_xor<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::xor_fetch(m_data, rhs);
}
return op_fetch([&](T& v)
{
v ^= rhs;
});
}
template<typename T2>
auto operator ^=(const T2& rhs)
auto operator ^=(const type& rhs)
{
return atomic_op(atomic_xor<type, T2>{}, rhs);
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::xor_fetch(m_data, rhs);
}
return atomic_op([&](T& v)
{
return v ^= rhs;
});
}
auto operator ++()
{
return atomic_op(atomic_pre_inc<type>{});
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::inc_fetch(m_data);
}
return atomic_op([](T& v)
{
return ++v;
});
}
auto operator --()
{
return atomic_op(atomic_pre_dec<type>{});
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::dec_fetch(m_data);
}
return atomic_op([](T& v)
{
return --v;
});
}
auto operator ++(int)
{
return atomic_op(atomic_post_inc<type>{});
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_inc(m_data);
}
return atomic_op([](T& v)
{
return v++;
});
}
auto operator --(int)
{
return atomic_op(atomic_post_dec<type>{});
}
if constexpr(std::is_integral<type>::value)
{
return atomic_storage<type>::fetch_dec(m_data);
}
template<typename T2 = T>
auto test_and_set(const T2& rhs)
{
return atomic_op(atomic_test_and_set<type, T2>{}, rhs);
}
template<typename T2 = T>
auto test_and_reset(const T2& rhs)
{
return atomic_op(atomic_test_and_reset<type, T2>{}, rhs);
}
template<typename T2 = T>
auto test_and_complement(const T2& rhs)
{
return atomic_op(atomic_test_and_complement<type, T2>{}, rhs);
}
// Minimal pointer support (TODO: must forward operator ->())
type operator ->() const
{
return load();
}
// Minimal array support
template<typename I = std::size_t>
auto operator [](const I& index) const -> decltype(std::declval<const type>()[std::declval<I>()])
{
return load()[index];
return atomic_op([](T& v)
{
return v--;
});
}
};

View file

@ -799,34 +799,34 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
#ifdef _WIN32
DWORD access = 0;
if (test(mode & fs::read)) access |= GENERIC_READ;
if (test(mode & fs::write)) access |= DELETE | (test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE);
if (mode & fs::read) access |= GENERIC_READ;
if (mode & fs::write) access |= DELETE | (mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE);
DWORD disp = 0;
if (test(mode & fs::create))
if (mode & fs::create)
{
disp =
test(mode & fs::excl) ? CREATE_NEW :
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
mode & fs::excl ? CREATE_NEW :
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
}
else
{
if (test(mode & fs::excl))
if (mode & fs::excl)
{
g_tls_error = error::inval;
return;
}
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
}
DWORD share = 0;
if (!test(mode, fs::unread) || !test(mode & fs::write))
if (!(mode & fs::unread) || !(mode & fs::write))
{
share |= FILE_SHARE_READ;
}
if (!test(mode, fs::lock + fs::unread) || !test(mode & fs::write))
if (!(mode & (fs::lock + fs::unread)) || !(mode & fs::write))
{
share |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
@ -949,18 +949,18 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
#else
int flags = 0;
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
else if (test(mode & fs::read)) flags |= O_RDONLY;
else if (test(mode & fs::write)) flags |= O_WRONLY;
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
else if (mode & fs::read) flags |= O_RDONLY;
else if (mode & fs::write) flags |= O_WRONLY;
if (test(mode & fs::append)) flags |= O_APPEND;
if (test(mode & fs::create)) flags |= O_CREAT;
if (test(mode & fs::trunc) && !test(mode, fs::lock + fs::unread)) flags |= O_TRUNC;
if (test(mode & fs::excl)) flags |= O_EXCL;
if (mode & fs::append) flags |= O_APPEND;
if (mode & fs::create) flags |= O_CREAT;
if (mode & fs::trunc && !(mode & (fs::lock + fs::unread))) flags |= O_TRUNC;
if (mode & fs::excl) flags |= O_EXCL;
int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
if (test(mode & fs::write) && test(mode & fs::unread))
if (mode & fs::write && mode & fs::unread)
{
perm = 0;
}
@ -973,14 +973,14 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
if (test(mode & fs::write) && test(mode, fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
if (mode & fs::write && mode & (fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
{
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
::close(fd);
return;
}
if (test(mode & fs::trunc) && test(mode, fs::lock + fs::unread))
if (mode & fs::trunc && mode & (fs::lock + fs::unread))
{
// Postpone truncation in order to avoid using O_TRUNC on a locked file
::ftruncate(fd, 0);

View file

@ -1,27 +1,8 @@
#pragma once
/*
This header helps to extend scoped enum types (enum class) in two possible ways:
1) Enabling bitwise operators for enums
2) Advanced bs_t<> template (this converts enum type to another "bitset" enum type)
To enable bitwise operators, enum scope must contain `__bitwise_ops` entry.
enum class flags
{
__bitwise_ops, // Not essential, but recommended to put it first
flag1 = 1 << 0,
flag2 = 1 << 1,
};
Examples:
`flags::flag1 | flags::flag2` - bitwise OR
`flags::flag1 & flags::flag2` - bitwise AND
`flags::flag1 ^ flags::flag2` - bitwise XOR
`~flags::flag1` - bitwise NEG
To enable bs_t<> template, enum scope must contain `__bitset_enum_max` entry.
This header implements bs_t<> class for scoped enum types (enum class).
To enable bs_t<>, enum scope must contain `__bitset_enum_max` entry.
enum class flagzz : u32
{
@ -31,40 +12,42 @@ enum class flagzz : u32
__bitset_enum_max // It must be the last value
};
Now some operators are enabled for two enum types: `flagzz` and `bs_t<flagzz>`.
These are very different from previously described bitwise operators.
This also enables helper operators for this enum type.
Examples:
`+flagzz::flag1` - unary `+` operator convert flagzz value to bs_t<flagzz>
`flagzz::flag1 + flagzz::flag2` - bitset union
`flagzz::flag1 - flagzz::flag2` - bitset difference
Intersection (&) and symmetric difference (^) is also available.
*/
#include "types.h"
#include "Atomic.h"
// Helper template
template<typename T>
struct bs_base
template <typename T>
class atomic_bs_t;
// Bitset type for enum class with available bits [0, T::__bitset_enum_max)
template <typename T>
class bs_t final
{
public:
// Underlying type
using under = std::underlying_type_t<T>;
// Actual bitset type
enum class type : under
{
null = 0, // Empty bitset
private:
// Underlying value
under m_data;
__bitset_set_type = 0 // SFINAE marker
};
friend class atomic_bs_t<T>;
public:
static constexpr std::size_t bitmax = sizeof(T) * 8;
static constexpr std::size_t bitsize = static_cast<under>(T::__bitset_enum_max);
static_assert(std::is_enum<T>::value, "bs_t<> error: invalid type (must be enum)");
static_assert(!bitsize || bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
static_assert(bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
static_assert(bitsize != bitmax || std::is_unsigned<under>::value, "bs_t<> error: invalid __bitset_enum_max (sign bit)");
// Helper function
static constexpr under shift(T value)
@ -72,662 +55,328 @@ struct bs_base
return static_cast<under>(1) << static_cast<under>(value);
}
friend type& operator +=(type& lhs, type rhs)
bs_t() = default;
// Construct from a single bit
constexpr bs_t(T bit)
: m_data(shift(bit))
{
reinterpret_cast<under&>(lhs) |= static_cast<under>(rhs);
return lhs;
}
friend type& operator -=(type& lhs, type rhs)
// Test for empty bitset
constexpr explicit operator bool() const
{
reinterpret_cast<under&>(lhs) &= ~static_cast<under>(rhs);
return lhs;
return m_data != 0;
}
friend type& operator &=(type& lhs, type rhs)
// Extract underlying data
constexpr explicit operator under() const
{
reinterpret_cast<under&>(lhs) &= static_cast<under>(rhs);
return lhs;
return m_data;
}
friend type& operator ^=(type& lhs, type rhs)
// Copy
constexpr bs_t operator +() const
{
reinterpret_cast<under&>(lhs) ^= static_cast<under>(rhs);
return lhs;
return *this;
}
friend type& operator +=(type& lhs, T rhs)
constexpr bs_t& operator +=(bs_t rhs)
{
reinterpret_cast<under&>(lhs) |= shift(rhs);
return lhs;
m_data |= static_cast<under>(rhs);
return *this;
}
friend type& operator -=(type& lhs, T rhs)
constexpr bs_t& operator -=(bs_t rhs)
{
reinterpret_cast<under&>(lhs) &= ~shift(rhs);
return lhs;
m_data &= ~static_cast<under>(rhs);
return *this;
}
friend type& operator &=(type& lhs, T rhs)
constexpr bs_t& operator &=(bs_t rhs)
{
reinterpret_cast<under&>(lhs) &= shift(rhs);
return lhs;
m_data &= static_cast<under>(rhs);
return *this;
}
friend type& operator ^=(type& lhs, T rhs)
constexpr bs_t& operator ^=(bs_t rhs)
{
reinterpret_cast<under&>(lhs) ^= shift(rhs);
return lhs;
m_data ^= static_cast<under>(rhs);
return *this;
}
friend constexpr type operator +(type lhs, type rhs)
constexpr bs_t operator +(bs_t rhs) const
{
return static_cast<type>(static_cast<under>(lhs) | static_cast<under>(rhs));
bs_t r{};
r.m_data = m_data | rhs.m_data;
return r;
}
friend constexpr type operator -(type lhs, type rhs)
constexpr bs_t operator -(bs_t rhs) const
{
return static_cast<type>(static_cast<under>(lhs) & ~static_cast<under>(rhs));
bs_t r{};
r.m_data = m_data & ~rhs.m_data;
return r;
}
friend constexpr type operator &(type lhs, type rhs)
constexpr bs_t operator &(bs_t rhs) const
{
return static_cast<type>(static_cast<under>(lhs) & static_cast<under>(rhs));
bs_t r{};
r.m_data = m_data & rhs.m_data;
return r;
}
friend constexpr type operator ^(type lhs, type rhs)
constexpr bs_t operator ^(bs_t rhs) const
{
return static_cast<type>(static_cast<under>(lhs) ^ static_cast<under>(rhs));
bs_t r{};
r.m_data = m_data ^ rhs.m_data;
return r;
}
friend constexpr type operator &(type lhs, T rhs)
constexpr bool operator ==(bs_t rhs) const
{
return static_cast<type>(static_cast<under>(lhs) & shift(rhs));
return m_data == rhs.m_data;
}
friend constexpr type operator ^(type lhs, T rhs)
constexpr bool operator !=(bs_t rhs) const
{
return static_cast<type>(static_cast<under>(lhs) ^ shift(rhs));
return m_data != rhs.m_data;
}
friend constexpr type operator &(T lhs, type rhs)
constexpr bool test(bs_t rhs) const
{
return static_cast<type>(shift(lhs) & static_cast<under>(rhs));
return (m_data & rhs.m_data) != 0;
}
friend constexpr type operator ^(T lhs, type rhs)
constexpr bool test_and_set(T bit)
{
return static_cast<type>(shift(lhs) ^ static_cast<under>(rhs));
bool r = (m_data & shift(bit)) != 0;
m_data |= shift(bit);
return r;
}
friend constexpr bool operator ==(T lhs, type rhs)
constexpr bool test_and_reset(T bit)
{
return shift(lhs) == rhs;
bool r = (m_data & shift(bit)) != 0;
m_data &= ~shift(bit);
return r;
}
friend constexpr bool operator ==(type lhs, T rhs)
constexpr bool test_and_complement(T bit)
{
return lhs == shift(rhs);
}
friend constexpr bool operator !=(T lhs, type rhs)
{
return shift(lhs) != rhs;
}
friend constexpr bool operator !=(type lhs, T rhs)
{
return lhs != shift(rhs);
}
friend constexpr bool test(type value)
{
return static_cast<under>(value) != 0;
}
friend constexpr bool test(type lhs, type rhs)
{
return (static_cast<under>(lhs) & static_cast<under>(rhs)) != 0;
}
friend constexpr bool test(type lhs, T rhs)
{
return (static_cast<under>(lhs) & shift(rhs)) != 0;
}
friend constexpr bool test(T lhs, type rhs)
{
return (shift(lhs) & static_cast<under>(rhs)) != 0;
}
friend bool test_and_set(type& lhs, type rhs)
{
return test_and_set(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
}
friend bool test_and_set(type& lhs, T rhs)
{
return test_and_set(reinterpret_cast<under&>(lhs), shift(rhs));
}
friend bool test_and_reset(type& lhs, type rhs)
{
return test_and_reset(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
}
friend bool test_and_reset(type& lhs, T rhs)
{
return test_and_reset(reinterpret_cast<under&>(lhs), shift(rhs));
}
friend bool test_and_complement(type& lhs, type rhs)
{
return test_and_complement(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
}
friend bool test_and_complement(type& lhs, T rhs)
{
return test_and_complement(reinterpret_cast<under&>(lhs), shift(rhs));
bool r = (m_data & shift(bit)) != 0;
m_data ^= shift(bit);
return r;
}
};
// Bitset type for enum class with available bits [0, T::__bitset_enum_max)
template<typename T>
using bs_t = typename bs_base<T>::type;
// Unary '+' operator: promote plain enum value to bitset value
template<typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator +(T value)
template <typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator +(T bit)
{
return static_cast<bs_t<T>>(bs_base<T>::shift(value));
return bit;
}
// Binary '+' operator: bitset union
template<typename T, typename = decltype(T::__bitset_enum_max)>
template <typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator +(T lhs, T rhs)
{
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) | bs_base<T>::shift(rhs));
}
// Binary '+' operator: bitset union
template<typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator +(typename bs_base<T>::type lhs, T rhs)
{
return static_cast<bs_t<T>>(static_cast<typename bs_base<T>::under>(lhs) | bs_base<T>::shift(rhs));
}
// Binary '+' operator: bitset union
template<typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator +(T lhs, typename bs_base<T>::type rhs)
{
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) | static_cast<typename bs_base<T>::under>(rhs));
return bs_t<T>(lhs) + bs_t<T>(rhs);
}
// Binary '-' operator: bitset difference
template<typename T, typename = decltype(T::__bitset_enum_max)>
template <typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator -(T lhs, T rhs)
{
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) & ~bs_base<T>::shift(rhs));
return bs_t<T>(lhs) - bs_t<T>(rhs);
}
// Binary '-' operator: bitset difference
template<typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator -(typename bs_base<T>::type lhs, T rhs)
// Binary '&' operator: bitset intersection
template <typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator &(T lhs, T rhs)
{
return static_cast<bs_t<T>>(static_cast<typename bs_base<T>::under>(lhs) & ~bs_base<T>::shift(rhs));
return bs_t<T>(lhs) & bs_t<T>(rhs);
}
// Binary '-' operator: bitset difference
template<typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator -(T lhs, typename bs_base<T>::type rhs)
// Binary '^' operator: bitset symmetric difference
template <typename T, typename = decltype(T::__bitset_enum_max)>
constexpr bs_t<T> operator ^(T lhs, T rhs)
{
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) & ~static_cast<typename bs_base<T>::under>(rhs));
return bs_t<T>(lhs) ^ bs_t<T>(rhs);
}
template<typename BS, typename T>
struct atomic_add<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
// Atomic bitset specialization with optimized operations
template <typename T>
class atomic_bs_t : public atomic_t<::bs_t<T>> // TODO: true specialization
{
using under = typename bs_base<T>::under;
// Corresponding bitset type
using bs_t = ::bs_t<T>;
static inline bs_t<T> op1(bs_t<T>& left, T right)
// Base class
using base = atomic_t<::bs_t<T>>;
// Use underlying m_data
using base::m_data;
public:
// Underlying type
using under = typename bs_t::under;
atomic_bs_t() = default;
atomic_bs_t(const atomic_bs_t&) = delete;
atomic_bs_t& operator =(const atomic_bs_t&) = delete;
explicit constexpr atomic_bs_t(bs_t value)
: base(value)
{
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
}
static constexpr auto fetch_op = &op1;
static inline bs_t<T> op2(bs_t<T>& left, T right)
explicit constexpr atomic_bs_t(T bit)
: base(bit)
{
return static_cast<bs_t<T>>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename BS, typename T>
struct atomic_sub<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
{
using under = typename bs_base<T>::under;
static inline bs_t<T> op1(bs_t<T>& left, T right)
{
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~bs_base<T>::shift(right)));
}
static constexpr auto fetch_op = &op1;
static inline bs_t<T> op2(bs_t<T>& left, T right)
{
return static_cast<bs_t<T>>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~bs_base<T>::shift(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename BS, typename T>
struct atomic_and<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
{
using under = typename bs_base<T>::under;
static inline bs_t<T> op1(bs_t<T>& left, T right)
{
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
}
static constexpr auto fetch_op = &op1;
static inline bs_t<T> op2(bs_t<T>& left, T right)
{
return static_cast<bs_t<T>>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename BS, typename T>
struct atomic_xor<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
{
using under = typename bs_base<T>::under;
static inline bs_t<T> op1(bs_t<T>& left, T right)
{
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
}
static constexpr auto fetch_op = &op1;
static inline bs_t<T> op2(bs_t<T>& left, T right)
{
return static_cast<bs_t<T>>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_add<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_sub<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_and<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_xor<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename BS, typename T>
struct atomic_test_and_set<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
{
using under = typename bs_base<T>::under;
static inline bool _op(bs_t<T>& left, T value)
{
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename BS, typename T>
struct atomic_test_and_reset<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
{
using under = typename bs_base<T>::under;
static inline bool _op(bs_t<T>& left, T value)
{
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename BS, typename T>
struct atomic_test_and_complement<BS, T, std::void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
{
using under = typename bs_base<T>::under;
static inline bool _op(bs_t<T>& left, T value)
{
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_set<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline bool _op(T& left, T value)
{
return atomic_storage<under>::test_and_set(reinterpret_cast<under&>(left), static_cast<under>(value));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_reset<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline bool _op(T& left, T value)
{
return atomic_storage<under>::test_and_reset(reinterpret_cast<under&>(left), static_cast<under>(value));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_complement<T, T, std::enable_if_t<sizeof(T::__bitset_set_type) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline bool _op(T& left, T value)
{
return atomic_storage<under>::test_and_complement(reinterpret_cast<under&>(left), static_cast<under>(value));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
// Binary '|' operator: bitwise OR
template<typename T, typename = decltype(T::__bitwise_ops)>
constexpr T operator |(T lhs, T rhs)
{
return static_cast<T>(std::underlying_type_t<T>(lhs) | std::underlying_type_t<T>(rhs));
}
// Binary '&' operator: bitwise AND
template<typename T, typename = decltype(T::__bitwise_ops)>
constexpr T operator &(T lhs, T rhs)
{
return static_cast<T>(std::underlying_type_t<T>(lhs) & std::underlying_type_t<T>(rhs));
}
// Binary '^' operator: bitwise XOR
template<typename T, typename = decltype(T::__bitwise_ops)>
constexpr T operator ^(T lhs, T rhs)
{
return static_cast<T>(std::underlying_type_t<T>(lhs) ^ std::underlying_type_t<T>(rhs));
}
// Unary '~' operator: bitwise NEG
template<typename T, typename = decltype(T::__bitwise_ops)>
constexpr T operator ~(T value)
{
return static_cast<T>(~std::underlying_type_t<T>(value));
}
// Bitwise OR assignment
template<typename T, typename = decltype(T::__bitwise_ops)>
inline T& operator |=(T& lhs, T rhs)
{
reinterpret_cast<std::underlying_type_t<T>&>(lhs) |= std::underlying_type_t<T>(rhs);
return lhs;
}
// Bitwise AND assignment
template<typename T, typename = decltype(T::__bitwise_ops)>
inline T& operator &=(T& lhs, T rhs)
{
reinterpret_cast<std::underlying_type_t<T>&>(lhs) &= std::underlying_type_t<T>(rhs);
return lhs;
}
// Bitwise XOR assignment
template<typename T, typename = decltype(T::__bitwise_ops)>
inline T& operator ^=(T& lhs, T rhs)
{
reinterpret_cast<std::underlying_type_t<T>&>(lhs) ^= std::underlying_type_t<T>(rhs);
return lhs;
}
template<typename T, typename = decltype(T::__bitwise_ops)>
constexpr bool test(T value)
{
return std::underlying_type_t<T>(value) != 0;
}
template<typename T, typename = decltype(T::__bitwise_ops)>
constexpr bool test(T lhs, T rhs)
{
return (std::underlying_type_t<T>(lhs) & std::underlying_type_t<T>(rhs)) != 0;
}
template<typename T, typename = decltype(T::__bitwise_ops)>
inline bool test_and_set(T& lhs, T rhs)
{
return test_and_set(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
}
template<typename T, typename = decltype(T::__bitwise_ops)>
inline bool test_and_reset(T& lhs, T rhs)
{
return test_and_reset(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
}
template<typename T, typename = decltype(T::__bitwise_ops)>
inline bool test_and_complement(T& lhs, T rhs)
{
return test_and_complement(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
}
template<typename T>
struct atomic_or<T, T, std::enable_if_t<sizeof(T::__bitwise_ops) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_and<T, T, std::enable_if_t<sizeof(T::__bitwise_ops) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_xor<T, T, std::enable_if_t<sizeof(T::__bitwise_ops) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline T op1(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto fetch_op = &op1;
static inline T op2(T& left, T right)
{
return static_cast<T>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_test_and_set<T, T, std::enable_if_t<sizeof(T::__bitwise_ops) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline bool _op(T& left, T value)
{
return atomic_storage<under>::test_and_set(reinterpret_cast<under&>(left), static_cast<under>(value));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_reset<T, T, std::enable_if_t<sizeof(T::__bitwise_ops) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline bool _op(T& left, T value)
{
return atomic_storage<under>::test_and_reset(reinterpret_cast<under&>(left), static_cast<under>(value));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_complement<T, T, std::enable_if_t<sizeof(T::__bitwise_ops) != 0 && std::is_enum<T>::value>>
{
using under = std::underlying_type_t<T>;
static inline bool _op(T& left, T value)
{
return atomic_storage<under>::test_and_complement(reinterpret_cast<under&>(left), static_cast<under>(value));
}
static constexpr auto fetch_op = &_op;
static constexpr auto op_fetch = &_op;
static constexpr auto atomic_op = &_op;
explicit operator bool() const
{
return static_cast<bool>(base::load());
}
explicit operator under() const
{
return static_cast<under>(base::load());
}
bs_t fetch_add(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::fetch_or(m_data.m_data, rhs.m_data);
return r;
}
bs_t add_fetch(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::or_fetch(m_data.m_data, rhs.m_data);
return r;
}
bs_t operator +=(const bs_t& rhs)
{
return add_fetch(rhs);
}
bs_t fetch_sub(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::fetch_and(m_data.m_data, ~rhs.m_data);
return r;
}
bs_t sub_fetch(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::and_fetch(m_data.m_data, ~rhs.m_data);
return r;
}
bs_t operator -=(const bs_t& rhs)
{
return sub_fetch(rhs);
}
bs_t fetch_and(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::fetch_and(m_data.m_data, rhs.m_data);
return r;
}
bs_t and_fetch(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::and_fetch(m_data.m_data, rhs.m_data);
return r;
}
bs_t operator &=(const bs_t& rhs)
{
return and_fetch(rhs);
}
bs_t fetch_xor(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::fetch_xor(m_data.m_data, rhs.m_data);
return r;
}
bs_t xor_fetch(const bs_t& rhs)
{
bs_t r;
r.m_data = atomic_storage<under>::xor_fetch(m_data.m_data, rhs.m_data);
return r;
}
bs_t operator ^=(const bs_t& rhs)
{
return xor_fetch(rhs);
}
auto fetch_or(const bs_t&) = delete;
auto or_fetch(const bs_t&) = delete;
auto operator |=(const bs_t&) = delete;
auto operator ++() = delete;
auto operator --() = delete;
auto operator ++(int) = delete;
auto operator --(int) = delete;
bs_t operator +(bs_t rhs) const
{
bs_t r{};
r.m_data = base::load().m_data | rhs.m_data;
return r;
}
bs_t operator -(bs_t rhs) const
{
bs_t r{};
r.m_data = base::load().m_data & ~rhs.m_data;
return r;
}
bs_t operator &(bs_t rhs) const
{
bs_t r{};
r.m_data = base::load().m_data & rhs.m_data;
return r;
}
bs_t operator ^(bs_t rhs) const
{
bs_t r{};
r.m_data = base::load().m_data ^ rhs.m_data;
return r;
}
bool test(const bs_t& rhs)
{
return base::load().test(rhs);
}
bool test_and_set(T rhs)
{
return atomic_storage<under>::bts(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
}
bool test_and_reset(T rhs)
{
return atomic_storage<under>::btr(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
}
bool test_and_complement(T rhs)
{
return atomic_storage<under>::btc(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
}
};

View file

@ -707,42 +707,6 @@ constexpr u32 size32(const T (&)[Size], const char* msg = nullptr)
return static_cast<u32>(Size);
}
template <typename T1, typename = std::enable_if_t<std::is_integral<T1>::value>>
constexpr bool test(const T1& value)
{
return value != 0;
}
template <typename T1, typename T2, typename = std::enable_if_t<std::is_integral<T1>::value && std::is_integral<T2>::value>>
constexpr bool test(const T1& lhs, const T2& rhs)
{
return (lhs & rhs) != 0;
}
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
inline bool test_and_set(T& lhs, const T2& rhs)
{
const bool result = (lhs & rhs) != 0;
lhs |= rhs;
return result;
}
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
inline bool test_and_reset(T& lhs, const T2& rhs)
{
const bool result = (lhs & rhs) != 0;
lhs &= ~rhs;
return result;
}
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
inline bool test_and_complement(T& lhs, const T2& rhs)
{
const bool result = (lhs & rhs) != 0;
lhs ^= rhs;
return result;
}
// Simplified hash algorithm for pointers. May be used in std::unordered_(map|set).
template <typename T, std::size_t Align = alignof(T)>
struct pointer_hash