mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 00:40:11 +00:00
Tidy endianness support (se_t) implementation
Move se_t and se_storage to util/endian.hpp Use single template instead of two specializations. Add minor optimization for MSVC. Remove v128 dependency. Try to enable intrinsics for unaligned data. Fix minor bug in u16/u32/u64 specializations.
This commit is contained in:
parent
c7c12941bc
commit
bd1a24b894
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/endian.hpp"
|
||||
#include <cstring>
|
||||
|
||||
// 128-bit vector type and also se_storage<> storage type
|
||||
@ -332,339 +333,13 @@ inline v128 operator~(const v128& other)
|
||||
return v128::from64(~other._u64[0], ~other._u64[1]);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t Align, std::size_t Size>
|
||||
struct se_storage
|
||||
{
|
||||
struct type
|
||||
{
|
||||
alignas(Align) std::byte data[Size];
|
||||
};
|
||||
|
||||
// Unoptimized generic byteswap for unaligned data
|
||||
static void reverse(u8* dst, const u8* src)
|
||||
{
|
||||
for (std::size_t i = 0; i < Size; i++)
|
||||
{
|
||||
dst[i] = src[Size - 1 - i];
|
||||
}
|
||||
}
|
||||
|
||||
static type to(const T& src)
|
||||
{
|
||||
type result;
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
|
||||
static T from(const type& src)
|
||||
{
|
||||
T result;
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 2, 2>
|
||||
{
|
||||
using type = u16;
|
||||
|
||||
static constexpr u16 swap(u16 src)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap16(src);
|
||||
#else
|
||||
return _byteswap_ushort(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u16 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u16>(src));
|
||||
}
|
||||
|
||||
static inline T from(u16 src)
|
||||
{
|
||||
return std::bit_cast<T, u16>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 4, 4>
|
||||
{
|
||||
using type = u32;
|
||||
|
||||
static constexpr u32 swap(u32 src)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap32(src);
|
||||
#else
|
||||
return _byteswap_ulong(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u32 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u32>(src));
|
||||
}
|
||||
|
||||
static inline T from(u32 src)
|
||||
{
|
||||
return std::bit_cast<T, u32>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 8, 8>
|
||||
{
|
||||
using type = u64;
|
||||
|
||||
static constexpr u64 swap(u64 src)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap64(src);
|
||||
#else
|
||||
return _byteswap_uint64(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u64 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u64>(src));
|
||||
}
|
||||
|
||||
static inline T from(u64 src)
|
||||
{
|
||||
return std::bit_cast<T, u64>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 16, 16>
|
||||
{
|
||||
using type = v128;
|
||||
|
||||
static inline v128 swap(const v128& src)
|
||||
{
|
||||
return v128::from64(se_storage<u64>::swap(src._u64[1]), se_storage<u64>::swap(src._u64[0]));
|
||||
}
|
||||
|
||||
static inline v128 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<v128>(src));
|
||||
}
|
||||
|
||||
static inline T from(const v128& src)
|
||||
{
|
||||
return std::bit_cast<T, v128>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
// Switched endianness
|
||||
template <typename T, std::size_t Align>
|
||||
class se_t<T, true, Align>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
using stype = typename se_storage<type, Align>::type;
|
||||
using storage = se_storage<type, Align>;
|
||||
|
||||
stype m_data;
|
||||
|
||||
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
|
||||
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
|
||||
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
|
||||
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
|
||||
|
||||
public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(const se_t& right) = default;
|
||||
|
||||
se_t(type value)
|
||||
: m_data(storage::to(value))
|
||||
{
|
||||
}
|
||||
|
||||
type value() const
|
||||
{
|
||||
return storage::from(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t&) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
{
|
||||
return m_data = storage::to(value), *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
{
|
||||
return storage::from(m_data);
|
||||
}
|
||||
};
|
||||
|
||||
// Native endianness
|
||||
template <typename T, std::size_t Align>
|
||||
class se_t<T, false, Align>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
using stype = typename se_storage<type, Align>::type;
|
||||
using storage = se_storage<type, Align>;
|
||||
|
||||
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
|
||||
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
|
||||
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
|
||||
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
|
||||
|
||||
stype m_data;
|
||||
|
||||
public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(type value)
|
||||
: m_data(std::bit_cast<stype>(value))
|
||||
{
|
||||
}
|
||||
|
||||
type value() const
|
||||
{
|
||||
return std::bit_cast<type>(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t& value) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
{
|
||||
m_data = std::bit_cast<stype>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
{
|
||||
return value();
|
||||
}
|
||||
};
|
||||
using stx::se_t;
|
||||
using stx::se_storage;
|
||||
|
||||
// se_t<> with native endianness
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using nse_t = se_t<T, false, Align>;
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator+=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value += right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator-=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value -= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator*=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value *= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator/=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value /= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator%=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value %= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator&=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value &= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator|=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value |= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator^=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value ^= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator<<=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value <<= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator>>=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value >>= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align> operator++(se_t<T, Se, Align>& left, int)
|
||||
{
|
||||
auto value = left.value();
|
||||
auto result = value++;
|
||||
left = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align> operator--(se_t<T, Se, Align>& left, int)
|
||||
{
|
||||
auto value = left.value();
|
||||
auto result = value--;
|
||||
left = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align>& operator++(se_t<T, Se, Align>& right)
|
||||
{
|
||||
auto value = right.value();
|
||||
return right = ++value;
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align>& operator--(se_t<T, Se, Align>& right)
|
||||
{
|
||||
auto value = right.value();
|
||||
return right = --value;
|
||||
}
|
||||
|
||||
#if IS_LE_MACHINE == 1
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using be_t = se_t<T, true, Align>;
|
||||
|
@ -191,12 +191,6 @@ namespace fmt
|
||||
const fmt_type_info* get_type_info();
|
||||
}
|
||||
|
||||
template <typename T, std::size_t Align = alignof(T), std::size_t Size = sizeof(T)>
|
||||
struct se_storage;
|
||||
|
||||
template <typename T, bool Se = true, std::size_t Align = alignof(T)>
|
||||
class se_t;
|
||||
|
||||
template <typename T>
|
||||
class atomic_t;
|
||||
|
||||
|
@ -335,7 +335,7 @@ static s32 savedata_check_args(u32 operation, u32 version, vm::cptr<char> dirNam
|
||||
|
||||
for (auto resv : setBuf->reserved)
|
||||
{
|
||||
if (resv.raw() != 0)
|
||||
if (resv)
|
||||
{
|
||||
// ****** sysutil savedata parameter error : 10 ******
|
||||
return 10;
|
||||
|
@ -229,7 +229,7 @@ void CgBinaryDisasm::TaskFP()
|
||||
for (u32 i = 0; i < (m_buffer_size - m_offset) / sizeof(u32); i++)
|
||||
{
|
||||
// Get BE data
|
||||
data[i] = be_t<u32>{data[i]}.raw();
|
||||
data[i] = std::bit_cast<u32, be_t<u32>>(data[i]);
|
||||
}
|
||||
|
||||
enum
|
||||
|
@ -375,7 +375,7 @@ public:
|
||||
verify(HERE), (m_buffer_size - m_offset) % sizeof(u32) == 0;
|
||||
for (u32 i = 0; i < (m_buffer_size - m_offset) / sizeof(u32); i++)
|
||||
{
|
||||
vdata[i] = be_t<u32>{vdata[i]}.raw();
|
||||
vdata[i] = std::bit_cast<u32, be_t<u32>>(vdata[i]);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < prog.ucodeSize / sizeof(u32); i++)
|
||||
|
@ -336,7 +336,7 @@ namespace rsx
|
||||
{
|
||||
//Endianness is swapped because common upload code expects input in BE
|
||||
//TODO: Implement fast upload path for LE inputs and do away with this
|
||||
element_push_buffer.push_back(be_t<u32>{index}.raw());
|
||||
element_push_buffer.push_back(std::bit_cast<u32, be_t<u32>>(index));
|
||||
}
|
||||
|
||||
u32 thread::get_push_buffer_index_count() const
|
||||
|
@ -65,9 +65,7 @@ namespace rsx
|
||||
rsx->sync_point_request = true;
|
||||
const u32 addr = get_address(method_registers.semaphore_offset_406e(), method_registers.semaphore_context_dma_406e());
|
||||
|
||||
// Get raw BE value
|
||||
arg = be_t<u32>{arg}.raw();
|
||||
const auto& sema = vm::_ref<atomic_t<nse_t<u32>>>(addr);
|
||||
const auto& sema = vm::_ref<atomic_t<be_t<u32>>>(addr);
|
||||
|
||||
// TODO: Remove vblank semaphore hack
|
||||
if (sema.load() == arg || addr == rsx->ctxt_addr + 0x30) return;
|
||||
@ -247,7 +245,7 @@ namespace rsx
|
||||
case rsx::vertex_base_type::ub:
|
||||
case rsx::vertex_base_type::ub256:
|
||||
// Get BE data
|
||||
arg = be_t<u32>{arg}.raw();
|
||||
arg = std::bit_cast<u32, be_t<u32>>(arg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1301,7 +1299,7 @@ namespace rsx
|
||||
else
|
||||
{
|
||||
std::vector<u8> temp(line_length * line_count);
|
||||
u8* buf = temp.data();
|
||||
u8* buf = temp.data();
|
||||
|
||||
for (u32 y = 0; y < line_count; ++y)
|
||||
{
|
||||
@ -1310,7 +1308,7 @@ namespace rsx
|
||||
src += in_pitch;
|
||||
}
|
||||
|
||||
buf = temp.data();
|
||||
buf = temp.data();
|
||||
|
||||
for (u32 y = 0; y < line_count; ++y)
|
||||
{
|
||||
|
297
rpcs3/util/endian.hpp
Normal file
297
rpcs3/util/endian.hpp
Normal file
@ -0,0 +1,297 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Utilities/types.h"
|
||||
|
||||
namespace stx
|
||||
{
|
||||
template <typename T, std::size_t Align = alignof(T), std::size_t Size = sizeof(T)>
|
||||
struct se_storage
|
||||
{
|
||||
struct type8
|
||||
{
|
||||
alignas(Align > alignof(T) ? alignof(T) : Align) unsigned char data[sizeof(T)];
|
||||
};
|
||||
|
||||
struct type64
|
||||
{
|
||||
alignas(8) std::uint64_t data[sizeof(T) / 8];
|
||||
};
|
||||
|
||||
using type = std::conditional_t<(Align >= 8 && sizeof(T) % 8 == 0), type64, type8>;
|
||||
|
||||
// Possibly unoptimized generic byteswap for unaligned data
|
||||
static constexpr type swap(const type& src) noexcept;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, alignof(std::uint16_t), 2>
|
||||
{
|
||||
using type = std::uint16_t;
|
||||
|
||||
static constexpr std::uint16_t swap(std::uint16_t src) noexcept
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap16(src);
|
||||
#else
|
||||
return _byteswap_ushort(src);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, alignof(std::uint32_t), 4>
|
||||
{
|
||||
using type = std::uint32_t;
|
||||
|
||||
static constexpr std::uint32_t swap(std::uint32_t src) noexcept
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap32(src);
|
||||
#else
|
||||
return _byteswap_ulong(src);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, alignof(std::uint64_t), 8>
|
||||
{
|
||||
using type = std::uint64_t;
|
||||
|
||||
static constexpr std::uint64_t swap(std::uint64_t src) noexcept
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap64(src);
|
||||
#else
|
||||
return _byteswap_uint64(src);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t Align, std::size_t Size>
|
||||
constexpr typename se_storage<T, Align, Size>::type se_storage<T, Align, Size>::swap(const type& src) noexcept
|
||||
{
|
||||
// Try to keep u16/u32/u64 optimizations at the cost of more bitcasts
|
||||
if constexpr (sizeof(T) == 1)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
else if constexpr (sizeof(T) == 2)
|
||||
{
|
||||
return std::bit_cast<type>(se_storage<std::uint16_t>::swap(std::bit_cast<std::uint16_t>(src)));
|
||||
}
|
||||
else if constexpr (sizeof(T) == 4)
|
||||
{
|
||||
return std::bit_cast<type>(se_storage<std::uint32_t>::swap(std::bit_cast<std::uint32_t>(src)));
|
||||
}
|
||||
else if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
return std::bit_cast<type>(se_storage<std::uint64_t>::swap(std::bit_cast<std::uint64_t>(src)));
|
||||
}
|
||||
else if constexpr (sizeof(T) % 8 == 0)
|
||||
{
|
||||
type64 tmp = std::bit_cast<type64>(src);
|
||||
type64 dst{};
|
||||
|
||||
// Swap u64 blocks
|
||||
for (std::size_t i = 0; i < sizeof(T) / 8; i++)
|
||||
{
|
||||
dst.data[i] = se_storage<std::uint64_t>::swap(tmp.data[sizeof(T) / 8 - 1 - i]);
|
||||
}
|
||||
|
||||
return std::bit_cast<type>(dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
type dst{};
|
||||
|
||||
// Swap by moving every byte
|
||||
for (std::size_t i = 0; i < sizeof(T); i++)
|
||||
{
|
||||
dst.data[i] = src.data[sizeof(T) - 1 - i];
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
// Endianness support template
|
||||
template <typename T, bool Swap, std::size_t Align = alignof(T)>
|
||||
class alignas(Align) se_t
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
using stype = typename se_storage<type, Align>::type;
|
||||
using storage = se_storage<type, Align>;
|
||||
|
||||
stype m_data;
|
||||
|
||||
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
|
||||
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
|
||||
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
|
||||
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
|
||||
|
||||
static stype to_data(type value) noexcept
|
||||
{
|
||||
if constexpr (Swap)
|
||||
{
|
||||
return storage::swap(std::bit_cast<stype>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::bit_cast<stype>(value);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(const se_t& right) = default;
|
||||
|
||||
se_t(type value) noexcept
|
||||
: m_data(to_data(value))
|
||||
{
|
||||
}
|
||||
|
||||
type value() const noexcept
|
||||
{
|
||||
if constexpr (Swap)
|
||||
{
|
||||
return std::bit_cast<type>(storage::swap(m_data));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::bit_cast<type>(m_data);
|
||||
}
|
||||
}
|
||||
|
||||
type get() const noexcept
|
||||
{
|
||||
return value();
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t&) = default;
|
||||
|
||||
se_t& operator=(type value) noexcept
|
||||
{
|
||||
m_data = to_data(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const noexcept
|
||||
{
|
||||
return value();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
static_assert(!type{});
|
||||
static_assert(!std::is_floating_point_v<type>);
|
||||
return !!std::bit_cast<type>(m_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator+=(const T1& rhs)
|
||||
{
|
||||
*this = value() + rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator-=(const T1& rhs)
|
||||
{
|
||||
*this = value() - rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator*=(const T1& rhs)
|
||||
{
|
||||
*this = value() * rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator/=(const T1& rhs)
|
||||
{
|
||||
*this = value() / rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator%=(const T1& rhs)
|
||||
{
|
||||
*this = value() % rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator&=(const T1& rhs)
|
||||
{
|
||||
*this = value() & rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator|=(const T1& rhs)
|
||||
{
|
||||
*this = value() | rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator^=(const T1& rhs)
|
||||
{
|
||||
*this = value() ^ rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator<<=(const T1& rhs)
|
||||
{
|
||||
*this = value() << rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
se_t& operator>>=(const T1& rhs)
|
||||
{
|
||||
*this = value() >> rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
se_t& operator++()
|
||||
{
|
||||
T value = *this;
|
||||
*this = ++value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
se_t& operator--()
|
||||
{
|
||||
T value = *this;
|
||||
*this = --value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T operator++(int)
|
||||
{
|
||||
T value = *this;
|
||||
T result = value++;
|
||||
*this = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
T operator--(int)
|
||||
{
|
||||
T value = *this;
|
||||
T result = value--;
|
||||
*this = value;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user