mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
Add imp for subsequent refactoring
This commit is contained in:
parent
f3bf137668
commit
67776aea65
74
include/libipc/imp/aligned.h
Normal file
74
include/libipc/imp/aligned.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/aligned.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Defines the type suitable for use as uninitialized storage for types of given type.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "libipc/imp/byte.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief The type suitable for use as uninitialized storage for types of given type.
|
||||||
|
* std::aligned_storage is deprecated in C++23, so we define our own.
|
||||||
|
* \tparam T The type to be aligned.
|
||||||
|
* \tparam AlignT The alignment of the type.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/types/aligned_storage
|
||||||
|
* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1413r3.pdf
|
||||||
|
*/
|
||||||
|
template <typename T, std::size_t AlignT = alignof(T)>
|
||||||
|
class aligned {
|
||||||
|
alignas(AlignT) std::array<ipc::byte, sizeof(T)> storage_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief Returns a pointer to the aligned storage.
|
||||||
|
* \return A pointer to the aligned storage.
|
||||||
|
*/
|
||||||
|
T *ptr() noexcept {
|
||||||
|
return reinterpret_cast<T *>(storage_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns a pointer to the aligned storage.
|
||||||
|
* \return A pointer to the aligned storage.
|
||||||
|
*/
|
||||||
|
T const *ptr() const noexcept {
|
||||||
|
return reinterpret_cast<const T *>(storage_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns a reference to the aligned storage.
|
||||||
|
* \return A reference to the aligned storage.
|
||||||
|
*/
|
||||||
|
T &ref() noexcept {
|
||||||
|
return *ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns a reference to the aligned storage.
|
||||||
|
* \return A reference to the aligned storage.
|
||||||
|
*/
|
||||||
|
T const &ref() const noexcept {
|
||||||
|
return *ptr();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Rounds up the given value to the given alignment.
|
||||||
|
* \tparam T The type of the value.
|
||||||
|
* \param value The value to be rounded up.
|
||||||
|
* \param alignment The alignment to be rounded up to.
|
||||||
|
* \return The rounded up value.
|
||||||
|
* \see https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
constexpr T round_up(T value, T alignment) noexcept {
|
||||||
|
return (value + alignment - 1) & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
163
include/libipc/imp/byte.h
Normal file
163
include/libipc/imp/byte.h
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/byte.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Define the byte type.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef> // std::byte (since C++17)
|
||||||
|
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
#include "libipc/imp/span.h"
|
||||||
|
|
||||||
|
#if defined(LIBIPC_CPP_17) && defined(__cpp_lib_byte)
|
||||||
|
#define LIBIPC_CPP_LIB_BYTE_
|
||||||
|
#endif // __cpp_lib_byte
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
class byte;
|
||||||
|
|
||||||
|
namespace detail_byte {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_integral =
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_not_byte =
|
||||||
|
typename std::enable_if<!std::is_same<
|
||||||
|
typename std::remove_cv<T>::type, byte>::value>::type;
|
||||||
|
|
||||||
|
} // namespace detail_byte
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A distinct type that implements the concept of byte as specified in the C++ language definition.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/types/byte
|
||||||
|
*/
|
||||||
|
class byte {
|
||||||
|
std::uint8_t bits_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
byte() noexcept = default;
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr byte(T v) noexcept
|
||||||
|
: bits_(static_cast<std::uint8_t>(v)) {}
|
||||||
|
|
||||||
|
#ifdef LIBIPC_CPP_LIB_BYTE_
|
||||||
|
constexpr byte(std::byte b) noexcept
|
||||||
|
: byte(std::to_integer<std::uint8_t>(b)) {}
|
||||||
|
#endif // LIBIPC_CPP_LIB_BYTE_
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr operator T() const noexcept {
|
||||||
|
return static_cast<T>(bits_);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIBIPC_CPP_LIB_BYTE_
|
||||||
|
constexpr operator std::byte() const noexcept {
|
||||||
|
/// \brief C++17 relaxed enum class initialization rules.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/language/enum#enum_relaxed_init_cpp17
|
||||||
|
return std::byte{bits_};
|
||||||
|
}
|
||||||
|
#endif // LIBIPC_CPP_LIB_BYTE_
|
||||||
|
|
||||||
|
friend bool operator==(byte const &lhs, byte const &rhs) noexcept {
|
||||||
|
return lhs.bits_ == rhs.bits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(byte const &lhs, byte const &rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Non-member functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr T to_integer(byte b) noexcept {
|
||||||
|
return T(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief std::operator<<, operator>>
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr byte operator<<(byte b, T shift) noexcept {
|
||||||
|
return byte(to_integer<unsigned>(b) << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr byte operator>>(byte b, T shift) noexcept {
|
||||||
|
return byte(to_integer<unsigned>(b) >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief std::operator<<=, operator>>=
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr byte &operator<<=(byte &b, T shift) noexcept {
|
||||||
|
return b = b << shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_integral<T>>
|
||||||
|
constexpr byte &operator>>=(byte &b, T shift) noexcept {
|
||||||
|
return b = b >> shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief std::operator|, operator&, operator^, operator~
|
||||||
|
|
||||||
|
constexpr byte operator|(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) | to_integer<unsigned>(r)); }
|
||||||
|
constexpr byte operator&(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) & to_integer<unsigned>(r)); }
|
||||||
|
constexpr byte operator^(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) ^ to_integer<unsigned>(r)); }
|
||||||
|
constexpr byte operator~(byte b) noexcept { return byte(~to_integer<unsigned>(b)); }
|
||||||
|
|
||||||
|
/// \brief std::operator|=, operator&=, operator^=
|
||||||
|
|
||||||
|
constexpr byte &operator|=(byte &l, byte r) noexcept { return l = l | r; }
|
||||||
|
constexpr byte &operator&=(byte &l, byte r) noexcept { return l = l & r; }
|
||||||
|
constexpr byte &operator^=(byte &l, byte r) noexcept { return l = l ^ r; }
|
||||||
|
|
||||||
|
/// \brief Cast pointer to byte*.
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_not_byte<T>>
|
||||||
|
byte *byte_cast(T *p) noexcept {
|
||||||
|
return reinterpret_cast<byte *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_not_byte<T>>
|
||||||
|
byte const *byte_cast(T const *p) noexcept {
|
||||||
|
return reinterpret_cast<byte const *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Cast byte* to a pointer of another type.
|
||||||
|
|
||||||
|
template <typename T, typename = detail_byte::is_not_byte<T>>
|
||||||
|
T *byte_cast(byte *p) noexcept {
|
||||||
|
if (reinterpret_cast<std::size_t>(p) % alignof(T) != 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<T *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U = typename std::add_const<T>::type,
|
||||||
|
typename = detail_byte::is_not_byte<T>>
|
||||||
|
U *byte_cast(byte const *p) noexcept {
|
||||||
|
if (reinterpret_cast<std::size_t>(p) % alignof(T) != 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<U *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Converts a span into a view of its underlying bytes.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/container/span/as_bytes
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename Byte = typename std::conditional<std::is_const<T>::value, byte const, byte>::type>
|
||||||
|
auto as_bytes(span<T> s) noexcept -> span<Byte> {
|
||||||
|
return {byte_cast(s.data()), s.size_bytes()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
226
include/libipc/imp/detect_plat.h
Normal file
226
include/libipc/imp/detect_plat.h
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/detect_plat.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Define platform detection related interfaces.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// \brief OS check.
|
||||||
|
|
||||||
|
#if defined(WINCE) || defined(_WIN32_WCE)
|
||||||
|
# define LIBIPC_OS_WINCE
|
||||||
|
#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
||||||
|
(defined(__x86_64) && defined(__MSYS__))
|
||||||
|
#define LIBIPC_OS_WIN64
|
||||||
|
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
|
||||||
|
defined(__NT__) || defined(__MSYS__)
|
||||||
|
# define LIBIPC_OS_WIN32
|
||||||
|
#elif defined(__QNX__) || defined(__QNXNTO__)
|
||||||
|
# define LIBIPC_OS_QNX
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
# define LIBIPC_OS_APPLE
|
||||||
|
#elif defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
# define LIBIPC_OS_ANDROID
|
||||||
|
#elif defined(__linux__) || defined(__linux)
|
||||||
|
# define LIBIPC_OS_LINUX
|
||||||
|
#elif defined(_POSIX_VERSION)
|
||||||
|
# define LIBIPC_OS_POSIX
|
||||||
|
#else
|
||||||
|
# error "This OS is unsupported."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(LIBIPC_OS_WIN32) || defined(LIBIPC_OS_WIN64) || \
|
||||||
|
defined(LIBIPC_OS_WINCE)
|
||||||
|
# define LIBIPC_OS_WIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Compiler check.
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define LIBIPC_CC_MSVC _MSC_VER
|
||||||
|
# define LIBIPC_CC_MSVC_2015 1900
|
||||||
|
# define LIBIPC_CC_MSVC_2017 1910
|
||||||
|
# define LIBIPC_CC_MSVC_2019 1920
|
||||||
|
# define LIBIPC_CC_MSVC_2022 1930
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# define LIBIPC_CC_GNUC __GNUC__
|
||||||
|
# if defined(__clang__)
|
||||||
|
# define LIBIPC_CC_CLANG
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
# error "This compiler is unsupported."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Instruction set.
|
||||||
|
/// \see https://sourceforge.net/p/predef/wiki/Architectures/
|
||||||
|
|
||||||
|
#if defined(_M_X64) || defined(_M_AMD64) || \
|
||||||
|
defined(__x86_64__) || defined(__x86_64) || \
|
||||||
|
defined(__amd64__) || defined(__amd64)
|
||||||
|
# define LIBIPC_INSTR_X64
|
||||||
|
#elif defined(_M_IA64) || defined(__IA64__) || defined(_IA64) || \
|
||||||
|
defined(__ia64__) || defined(__ia64)
|
||||||
|
# define LIBIPC_INSTR_I64
|
||||||
|
#elif defined(_M_IX86) || defined(_X86_) || defined(__i386__) || defined(__i386)
|
||||||
|
# define LIBIPC_INSTR_X86
|
||||||
|
#elif defined(_M_ARM64) || defined(__arm64__) || defined(__aarch64__)
|
||||||
|
# define LIBIPC_INSTR_ARM64
|
||||||
|
#elif defined(_M_ARM) || defined(_ARM) || defined(__arm__) || defined(__arm)
|
||||||
|
# define LIBIPC_INSTR_ARM32
|
||||||
|
#else
|
||||||
|
# error "This instruction set is unsupported."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(LIBIPC_INSTR_X86) || defined(LIBIPC_INSTR_X64)
|
||||||
|
# define LIBIPC_INSTR_X86_64
|
||||||
|
#elif defined(LIBIPC_INSTR_ARM32) || defined(LIBIPC_INSTR_ARM64)
|
||||||
|
# define LIBIPC_INSTR_ARM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Byte order.
|
||||||
|
|
||||||
|
#if defined(__BYTE_ORDER__)
|
||||||
|
# define LIBIPC_ENDIAN_BIG (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
|
# define LIBIPC_ENDIAN_LIT (!LIBIPC_ENDIAN_BIG)
|
||||||
|
#else
|
||||||
|
# define LIBIPC_ENDIAN_BIG (0)
|
||||||
|
# define LIBIPC_ENDIAN_LIT (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief C++ version.
|
||||||
|
|
||||||
|
#if (__cplusplus >= 202002L) && !defined(LIBIPC_CPP_20)
|
||||||
|
# define LIBIPC_CPP_20
|
||||||
|
#endif
|
||||||
|
#if (__cplusplus >= 201703L) && !defined(LIBIPC_CPP_17)
|
||||||
|
# define LIBIPC_CPP_17
|
||||||
|
#endif
|
||||||
|
#if /*(__cplusplus >= 201402L) &&*/ !defined(LIBIPC_CPP_14)
|
||||||
|
# define LIBIPC_CPP_14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_CPP_20) && \
|
||||||
|
!defined(LIBIPC_CPP_17) && \
|
||||||
|
!defined(LIBIPC_CPP_14)
|
||||||
|
# error "This C++ version is unsupported."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Feature cross-platform adaptation.
|
||||||
|
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
# define LIBIPC_INLINE_CONSTEXPR inline constexpr
|
||||||
|
#else
|
||||||
|
# define LIBIPC_INLINE_CONSTEXPR constexpr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief C++ attributes.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/language/attributes
|
||||||
|
|
||||||
|
#if defined(__has_cpp_attribute)
|
||||||
|
# if __has_cpp_attribute(fallthrough)
|
||||||
|
# define LIBIPC_FALLTHROUGH [[fallthrough]]
|
||||||
|
# endif
|
||||||
|
# if __has_cpp_attribute(maybe_unused)
|
||||||
|
# define LIBIPC_UNUSED [[maybe_unused]]
|
||||||
|
# endif
|
||||||
|
# if __has_cpp_attribute(likely)
|
||||||
|
# define LIBIPC_LIKELY(...) (__VA_ARGS__) [[likely]]
|
||||||
|
# endif
|
||||||
|
# if __has_cpp_attribute(unlikely)
|
||||||
|
# define LIBIPC_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
|
||||||
|
# endif
|
||||||
|
# if __has_cpp_attribute(nodiscard)
|
||||||
|
# define LIBIPC_NODISCARD [[nodiscard]]
|
||||||
|
# endif
|
||||||
|
# if __has_cpp_attribute(assume)
|
||||||
|
# define LIBIPC_ASSUME(...) [[assume(__VA_ARGS__)]]
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_FALLTHROUGH)
|
||||||
|
# if defined(LIBIPC_CC_GNUC)
|
||||||
|
# define LIBIPC_FALLTHROUGH __attribute__((__fallthrough__))
|
||||||
|
# else
|
||||||
|
# define LIBIPC_FALLTHROUGH
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_UNUSED)
|
||||||
|
# if defined(LIBIPC_CC_GNUC)
|
||||||
|
# define LIBIPC_UNUSED __attribute__((__unused__))
|
||||||
|
# elif defined(LIBIPC_CC_MSVC)
|
||||||
|
# define LIBIPC_UNUSED __pragma(warning(suppress: 4100 4101 4189))
|
||||||
|
# else
|
||||||
|
# define LIBIPC_UNUSED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_LIKELY)
|
||||||
|
# if defined(__has_builtin)
|
||||||
|
# if __has_builtin(__builtin_expect)
|
||||||
|
# define LIBIPC_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_LIKELY)
|
||||||
|
# define LIBIPC_LIKELY(...) (__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_UNLIKELY)
|
||||||
|
# if defined(__has_builtin)
|
||||||
|
# if __has_builtin(__builtin_expect)
|
||||||
|
# define LIBIPC_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_UNLIKELY)
|
||||||
|
# define LIBIPC_UNLIKELY(...) (__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_NODISCARD)
|
||||||
|
/// \see https://stackoverflow.com/questions/4226308/msvc-equivalent-of-attribute-warn-unused-result
|
||||||
|
# if defined(LIBIPC_CC_GNUC) && (LIBIPC_CC_GNUC >= 4)
|
||||||
|
# define LIBIPC_NODISCARD __attribute__((warn_unused_result))
|
||||||
|
# elif defined(LIBIPC_CC_MSVC) && (LIBIPC_CC_MSVC >= 1700)
|
||||||
|
# define LIBIPC_NODISCARD _Check_return_
|
||||||
|
# else
|
||||||
|
# define LIBIPC_NODISCARD
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_ASSUME)
|
||||||
|
# if defined(__has_builtin)
|
||||||
|
# if __has_builtin(__builtin_assume)
|
||||||
|
/// \see https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-assume
|
||||||
|
# define LIBIPC_ASSUME(...) __builtin_assume(__VA_ARGS__)
|
||||||
|
# elif __has_builtin(__builtin_unreachable)
|
||||||
|
/// \see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005funreachable
|
||||||
|
# define LIBIPC_ASSUME(...) do { if (!(__VA_ARGS__)) __builtin_unreachable(); } while (false)
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_ASSUME)
|
||||||
|
# if defined(LIBIPC_CC_MSVC)
|
||||||
|
/// \see https://learn.microsoft.com/en-us/cpp/intrinsics/assume?view=msvc-140
|
||||||
|
# define LIBIPC_ASSUME(...) __assume(__VA_ARGS__)
|
||||||
|
# else
|
||||||
|
# define LIBIPC_ASSUME(...)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \see https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
|
||||||
|
/// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||||
|
/// https://stackoverflow.com/questions/6487013/programmatically-determine-whether-exceptions-are-enabled
|
||||||
|
#if defined(__cpp_exceptions) && __cpp_exceptions || \
|
||||||
|
defined(__EXCEPTIONS) || defined(_CPPUNWIND)
|
||||||
|
# define LIBIPC_TRY try
|
||||||
|
# define LIBIPC_CATCH(...) catch (__VA_ARGS__)
|
||||||
|
# define LIBIPC_THROW($EXCEPTION, ...) throw $EXCEPTION
|
||||||
|
#else
|
||||||
|
# define LIBIPC_TRY if (true)
|
||||||
|
# define LIBIPC_CATCH(...) else if (false)
|
||||||
|
# define LIBIPC_THROW($EXCEPTION, ...) return __VA_ARGS__
|
||||||
|
#endif
|
||||||
299
include/libipc/imp/generic.h
Normal file
299
include/libipc/imp/generic.h
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/generic.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Tools for generic programming.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <type_traits> // std::declval, std::true_type, std::false_type
|
||||||
|
#include <cstddef> // std::size_t
|
||||||
|
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Utility metafunction that maps a sequence of any types to the type void
|
||||||
|
* \see https://en.cppreference.com/w/cpp/types/void_t
|
||||||
|
*/
|
||||||
|
template <typename...>
|
||||||
|
using void_t = void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A type-list for generic programming.
|
||||||
|
*/
|
||||||
|
template <typename...>
|
||||||
|
struct types {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief To indicate that the contained object should be constructed in-place.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/utility/in_place
|
||||||
|
*/
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
using std::in_place_t;
|
||||||
|
using std::in_place;
|
||||||
|
#else /*!LIBIPC_CPP_17*/
|
||||||
|
struct in_place_t {
|
||||||
|
explicit in_place_t() = default;
|
||||||
|
};
|
||||||
|
constexpr in_place_t in_place {};
|
||||||
|
#endif/*!LIBIPC_CPP_17*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A general pattern for supporting customisable functions
|
||||||
|
* \see https://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/p1895r0.pdf
|
||||||
|
*/
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
void tag_invoke();
|
||||||
|
|
||||||
|
struct tag_invoke_t {
|
||||||
|
template <typename T, typename... A>
|
||||||
|
constexpr auto operator()(T tag, A &&...args) const
|
||||||
|
noexcept(noexcept(tag_invoke(std::forward<T>(tag), std::forward<A>(args)...)))
|
||||||
|
-> decltype(tag_invoke(std::forward<T>(tag), std::forward<A>(args)...)) {
|
||||||
|
return tag_invoke(std::forward<T>(tag), std::forward<A>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
constexpr detail::tag_invoke_t tag_invoke {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Circumventing forwarding reference may override copy and move constructs.
|
||||||
|
* \see https://mpark.github.io/programming/2014/06/07/beware-of-perfect-forwarding-constructors/
|
||||||
|
*/
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T, typename... A>
|
||||||
|
struct is_same_first : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_same_first<T, T> : std::true_type {};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename... A>
|
||||||
|
using not_match =
|
||||||
|
typename std::enable_if<!detail::is_same_first<T,
|
||||||
|
typename std::decay<A>::type...>::value, bool>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Determines whether a type is specialized from a particular template.
|
||||||
|
*/
|
||||||
|
template <template <typename...> class Tt, typename T>
|
||||||
|
struct is_specialized : std::false_type {};
|
||||||
|
|
||||||
|
template <template <typename...> class Tt, typename... A>
|
||||||
|
struct is_specialized<Tt, Tt<A...>> : std::true_type {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Copy the cv qualifier and reference of the source type to the target type.
|
||||||
|
*/
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
struct copy_cvref {
|
||||||
|
using type = Des;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
struct copy_cvref<Src const, Des> {
|
||||||
|
using type = typename std::add_const<Des>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
struct copy_cvref<Src volatile, Des> {
|
||||||
|
using type = typename std::add_volatile<Des>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
struct copy_cvref<Src const volatile, Des> {
|
||||||
|
using type = typename std::add_cv<Des>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
struct copy_cvref<Src &, Des> {
|
||||||
|
using type = typename std::add_lvalue_reference<
|
||||||
|
typename copy_cvref<Src, Des>::type>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
struct copy_cvref<Src &&, Des> {
|
||||||
|
using type = typename std::add_rvalue_reference<
|
||||||
|
typename copy_cvref<Src, Des>::type>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src, typename Des>
|
||||||
|
using copy_cvref_t = typename copy_cvref<Src, Des>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns the size of the given range.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/iterator/size
|
||||||
|
*/
|
||||||
|
namespace detail_countof {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct trait_has_size {
|
||||||
|
private:
|
||||||
|
template <typename Type>
|
||||||
|
static std::true_type check(decltype(std::declval<Type>().size())*);
|
||||||
|
template <typename Type>
|
||||||
|
static std::false_type check(...);
|
||||||
|
public:
|
||||||
|
using type = decltype(check<T>(nullptr));
|
||||||
|
static constexpr auto value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct trait_has_Size {
|
||||||
|
private:
|
||||||
|
template <typename Type>
|
||||||
|
static std::true_type check(decltype(std::declval<Type>().Size())*);
|
||||||
|
template <typename Type>
|
||||||
|
static std::false_type check(...);
|
||||||
|
public:
|
||||||
|
using type = decltype(check<T>(nullptr));
|
||||||
|
static constexpr auto value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C, bool = trait_has_size<C>::value
|
||||||
|
, bool = trait_has_Size<C>::value>
|
||||||
|
struct trait;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct trait<T[N], false, false> {
|
||||||
|
static constexpr auto countof(T const (&)[N]) noexcept {
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C, bool B>
|
||||||
|
struct trait<C, true, B> {
|
||||||
|
static constexpr auto countof(C const &c) noexcept(noexcept(c.size())) {
|
||||||
|
return c.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
struct trait<C, false, true> {
|
||||||
|
static constexpr auto countof(C const &c) noexcept(noexcept(c.Size())) {
|
||||||
|
return c.Size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail_countof
|
||||||
|
|
||||||
|
template <typename C,
|
||||||
|
typename T = detail_countof::trait<C>,
|
||||||
|
typename R = decltype(T::countof(std::declval<C const &>()))>
|
||||||
|
constexpr R countof(C const &c) noexcept(noexcept(T::countof(c))) {
|
||||||
|
return T::countof(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns the data pointer of the given range.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/iterator/data
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace detail_dataof {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct trait_has_data {
|
||||||
|
private:
|
||||||
|
template <typename Type>
|
||||||
|
static std::true_type check(decltype(std::declval<Type>().data())*);
|
||||||
|
template <typename Type>
|
||||||
|
static std::false_type check(...);
|
||||||
|
public:
|
||||||
|
using type = decltype(check<T>(nullptr));
|
||||||
|
static constexpr auto value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct trait_has_Data {
|
||||||
|
private:
|
||||||
|
template <typename Type>
|
||||||
|
static std::true_type check(decltype(std::declval<Type>().Data())*);
|
||||||
|
template <typename Type>
|
||||||
|
static std::false_type check(...);
|
||||||
|
public:
|
||||||
|
using type = decltype(check<T>(nullptr));
|
||||||
|
static constexpr auto value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C, bool = trait_has_data<C>::value
|
||||||
|
, bool = trait_has_Data<C>::value>
|
||||||
|
struct trait;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct trait<T[N], false, false> {
|
||||||
|
static constexpr T const *dataof(T const (&arr)[N]) noexcept {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
static constexpr T *dataof(T (&arr)[N]) noexcept {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C, bool B>
|
||||||
|
struct trait<C, true, B> {
|
||||||
|
template <typename T>
|
||||||
|
static constexpr auto dataof(T &&c) noexcept(noexcept(c.data())) {
|
||||||
|
return std::forward<T>(c).data();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
struct trait<C, false, true> {
|
||||||
|
template <typename T>
|
||||||
|
static constexpr auto dataof(T &&c) noexcept(noexcept(c.Data())) {
|
||||||
|
return std::forward<T>(c).Data();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
struct trait<C, false, false> {
|
||||||
|
template <typename T>
|
||||||
|
static constexpr T const *dataof(std::initializer_list<T> il) noexcept {
|
||||||
|
return il.begin();
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
static constexpr T const *dataof(T const *p) noexcept {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail_dataof
|
||||||
|
|
||||||
|
template <typename C,
|
||||||
|
typename T = detail_dataof::trait<std::remove_cv_t<std::remove_reference_t<C>>>,
|
||||||
|
typename R = decltype(T::dataof(std::declval<C>()))>
|
||||||
|
constexpr R dataof(C &&c) noexcept(noexcept(T::dataof(std::forward<C>(c)))) {
|
||||||
|
return T::dataof(std::forward<C>(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Returns after converting the value to the underlying type of E.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/types/underlying_type
|
||||||
|
/// https://en.cppreference.com/w/cpp/utility/to_underlying
|
||||||
|
template <typename E>
|
||||||
|
constexpr auto underlyof(E e) noexcept {
|
||||||
|
return static_cast<std::underlying_type_t<E>>(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail_horrible_cast {
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
union temp {
|
||||||
|
std::decay_t<U> in;
|
||||||
|
T out;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail_horrible_cast
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
constexpr T horrible_cast(U &&in) noexcept {
|
||||||
|
return detail_horrible_cast::temp<T, U>{std::forward<U>(in)}.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
281
include/libipc/imp/span.h
Normal file
281
include/libipc/imp/span.h
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/span.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Describes an object that can refer to a contiguous sequence of objects.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
#include "libipc/imp/generic.h"
|
||||||
|
|
||||||
|
#if defined(LIBIPC_CPP_20) && defined(__cpp_lib_span)
|
||||||
|
#include <span>
|
||||||
|
#define LIBIPC_CPP_LIB_SPAN_
|
||||||
|
#endif // __cpp_lib_span
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace detail_span {
|
||||||
|
|
||||||
|
/// \brief Helper trait for span.
|
||||||
|
|
||||||
|
template <typename From, typename To>
|
||||||
|
using array_convertible = std::is_convertible<From(*)[], To(*)[]>;
|
||||||
|
|
||||||
|
template <typename From, typename To>
|
||||||
|
using is_array_convertible =
|
||||||
|
typename std::enable_if<array_convertible<From, To>::value>::type;
|
||||||
|
|
||||||
|
template <typename T, typename Ref>
|
||||||
|
using compatible_ref = array_convertible<typename std::remove_reference<Ref>::type, T>;
|
||||||
|
|
||||||
|
template <typename It>
|
||||||
|
using iter_reference_t = decltype(*std::declval<It&>());
|
||||||
|
|
||||||
|
template <typename T, typename It>
|
||||||
|
using is_compatible_iter =
|
||||||
|
typename std::enable_if<compatible_ref<T, iter_reference_t<It>>::value>::type;
|
||||||
|
|
||||||
|
template <typename From, typename To>
|
||||||
|
using is_inconvertible =
|
||||||
|
typename std::enable_if<!std::is_convertible<From, To>::value>::type;
|
||||||
|
|
||||||
|
template <typename S, typename I>
|
||||||
|
using is_sized_sentinel_for =
|
||||||
|
typename std::enable_if<std::is_convertible<decltype(std::declval<S>() - std::declval<I>()),
|
||||||
|
std::ptrdiff_t>::value>::type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_continuous_container =
|
||||||
|
decltype(dataof(std::declval<T>()), countof(std::declval<T>()));
|
||||||
|
|
||||||
|
/// \brief Obtain the address represented by p
|
||||||
|
/// without forming a reference to the object pointed to by p.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/memory/to_address
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T *to_address(T *ptr) noexcept {
|
||||||
|
static_assert(!std::is_function<T>::value, "ptr shouldn't a function pointer");
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr auto to_address(T const &ptr)
|
||||||
|
noexcept(noexcept(ptr.operator->()))
|
||||||
|
-> decltype(ptr.operator->()) {
|
||||||
|
return to_address(ptr.operator->());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail_span
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A simple implementation of span.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/container/span
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class span {
|
||||||
|
public:
|
||||||
|
using element_type = T;
|
||||||
|
using value_type = typename std::remove_cv<element_type>::type;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = element_type *;
|
||||||
|
using const_pointer = typename std::remove_const<element_type>::type const *;
|
||||||
|
using reference = element_type &;
|
||||||
|
using const_reference = typename std::remove_const<element_type>::type const &;
|
||||||
|
using iterator = pointer;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
pointer ptr_ {nullptr};
|
||||||
|
size_type extent_ {0};
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr span() noexcept = default;
|
||||||
|
constexpr span(span const &) noexcept = default;
|
||||||
|
#if (LIBIPC_CC_MSVC > LIBIPC_CC_MSVC_2015)
|
||||||
|
constexpr
|
||||||
|
#endif
|
||||||
|
span & operator=(span const &) noexcept = default;
|
||||||
|
|
||||||
|
template <typename It,
|
||||||
|
typename = detail_span::is_compatible_iter<T, It>>
|
||||||
|
constexpr span(It first, size_type count) noexcept
|
||||||
|
: ptr_ (detail_span::to_address(first))
|
||||||
|
, extent_(count) {}
|
||||||
|
|
||||||
|
template <typename It, typename End,
|
||||||
|
typename = detail_span::is_compatible_iter<T, It>,
|
||||||
|
typename = detail_span::is_sized_sentinel_for<End, It>,
|
||||||
|
typename = detail_span::is_inconvertible<End, size_type>>
|
||||||
|
constexpr span(It first, End last) noexcept(noexcept(last - first))
|
||||||
|
: ptr_ (detail_span::to_address(first))
|
||||||
|
, extent_(static_cast<size_type>(last - first)) {}
|
||||||
|
|
||||||
|
template <typename U,
|
||||||
|
typename = detail_span::is_continuous_container<U>,
|
||||||
|
typename = detail_span::is_array_convertible<std::remove_pointer_t<
|
||||||
|
decltype(dataof(std::declval<U>()))>, element_type>>
|
||||||
|
constexpr span(U &&u) noexcept(noexcept(dataof (std::forward<U>(u)),
|
||||||
|
countof(std::forward<U>(u))))
|
||||||
|
: ptr_ (dataof (std::forward<U>(u)))
|
||||||
|
, extent_(countof(std::forward<U>(u))) {}
|
||||||
|
|
||||||
|
template <typename U, std::size_t E,
|
||||||
|
typename = detail_span::is_array_convertible<U, element_type>>
|
||||||
|
constexpr span(U (&arr)[E]) noexcept
|
||||||
|
: span(static_cast<pointer>(arr), E) {}
|
||||||
|
|
||||||
|
template <typename U, std::size_t E,
|
||||||
|
typename = detail_span::is_array_convertible<U, element_type>>
|
||||||
|
constexpr span(std::array<U, E> &arr) noexcept
|
||||||
|
: span(static_cast<pointer>(arr.data()), E) {}
|
||||||
|
|
||||||
|
template <typename U, std::size_t E,
|
||||||
|
typename = detail_span::is_array_convertible<typename std::add_const<U>::type, element_type>>
|
||||||
|
constexpr span(std::array<U, E> const &arr) noexcept
|
||||||
|
: span(static_cast<pointer>(arr.data()), E) {}
|
||||||
|
|
||||||
|
template <typename U,
|
||||||
|
typename = detail_span::is_array_convertible<U, T>>
|
||||||
|
constexpr span(span<U> const &s) noexcept
|
||||||
|
: ptr_ (s.data())
|
||||||
|
, extent_(s.size()) {}
|
||||||
|
|
||||||
|
#ifdef LIBIPC_CPP_LIB_SPAN_
|
||||||
|
template <typename U, std::size_t E,
|
||||||
|
typename = detail_span::is_array_convertible<U, T>>
|
||||||
|
constexpr span(std::span<U, E> const &s) noexcept
|
||||||
|
: ptr_ (s.data())
|
||||||
|
, extent_(s.size()) {}
|
||||||
|
#endif // LIBIPC_CPP_LIB_SPAN_
|
||||||
|
|
||||||
|
constexpr size_type size() const noexcept {
|
||||||
|
return extent_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_type size_bytes() const noexcept {
|
||||||
|
return size() * sizeof(element_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool empty() const noexcept {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr pointer data() const noexcept {
|
||||||
|
return this->ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference front() const noexcept {
|
||||||
|
return *data();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference back() const noexcept {
|
||||||
|
return *(data() + (size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference operator[](size_type idx) const noexcept {
|
||||||
|
return *(data() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr iterator begin() const noexcept {
|
||||||
|
return iterator(data());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr iterator end() const noexcept {
|
||||||
|
return iterator(data() + this->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() const noexcept {
|
||||||
|
return reverse_iterator(this->end());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reverse_iterator rend() const noexcept {
|
||||||
|
return reverse_iterator(this->begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span first(size_type count) const noexcept {
|
||||||
|
return span(begin(), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span last(size_type count) const noexcept {
|
||||||
|
return span(end() - count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr span subspan(size_type offset, size_type count = (std::numeric_limits<size_type>::max)()) const noexcept {
|
||||||
|
return (offset >= size()) ? span() : span(begin() + offset, (std::min)(size() - offset, count));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Support for span equals comparison.
|
||||||
|
|
||||||
|
template <typename T, typename U,
|
||||||
|
typename = decltype(std::declval<T>() == std::declval<U>())>
|
||||||
|
bool operator==(span<T> a, span<U> b) noexcept {
|
||||||
|
if (a.size() != b.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < a.size(); ++i) {
|
||||||
|
if (a[i] != b[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Constructs an object of type T and wraps it in a span.
|
||||||
|
/// Before C++17, template argument deduction for class templates was not supported.
|
||||||
|
/// \see https://en.cppreference.com/w/cpp/language/template_argument_deduction
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto make_span(T *arr, std::size_t count) noexcept -> span<T> {
|
||||||
|
return {arr, count};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t E>
|
||||||
|
auto make_span(T (&arr)[E]) noexcept -> span<T> {
|
||||||
|
return {arr};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t E>
|
||||||
|
auto make_span(std::array<T, E> &arr) noexcept -> span<T> {
|
||||||
|
return {arr};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t E>
|
||||||
|
auto make_span(std::array<T, E> const &arr) noexcept -> span<typename std::add_const<T>::type> {
|
||||||
|
return {arr};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto make_span(std::vector<T> &arr) noexcept -> span<T> {
|
||||||
|
return {arr.data(), arr.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto make_span(std::vector<T> const &arr) noexcept -> span<typename std::add_const<T>::type> {
|
||||||
|
return {arr.data(), arr.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto make_span(std::initializer_list<T> list) noexcept -> span<typename std::add_const<T>::type> {
|
||||||
|
return {list.begin(), list.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto make_span(std::string &str) noexcept -> span<char> {
|
||||||
|
return {const_cast<char *>(str.data()), str.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto make_span(std::string const &str) noexcept -> span<char const> {
|
||||||
|
return {str.data(), str.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
146
include/libipc/imp/uninitialized.h
Normal file
146
include/libipc/imp/uninitialized.h
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/uninitialized.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Uninitialized memory algorithms.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <new> // placement-new
|
||||||
|
#include <type_traits> // std::enable_if_t
|
||||||
|
#include <utility> // std::forward
|
||||||
|
#include <memory> // std::construct_at, std::destroy_at, std::addressof
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
#include "libipc/imp/generic.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates an object at a given address, like 'construct_at' in c++20
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/construct_at
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename T, typename... A>
|
||||||
|
auto construct(void *p, A &&...args)
|
||||||
|
-> std::enable_if_t<::std::is_constructible<T, A...>::value, T *> {
|
||||||
|
#if defined(LIBIPC_CPP_20)
|
||||||
|
return std::construct_at(static_cast<T *>(p), std::forward<A>(args)...);
|
||||||
|
#else
|
||||||
|
return ::new (p) T(std::forward<A>(args)...);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... A>
|
||||||
|
auto construct(void *p, A &&...args)
|
||||||
|
-> std::enable_if_t<!::std::is_constructible<T, A...>::value, T *> {
|
||||||
|
return ::new (p) T{std::forward<A>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys an object at a given address, like 'destroy_at' in c++17
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/destroy_at
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void *destroy(T *p) noexcept {
|
||||||
|
if (p == nullptr) return nullptr;
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
std::destroy_at(p);
|
||||||
|
#else
|
||||||
|
p->~T();
|
||||||
|
#endif
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
void *destroy(T (*p)[N]) noexcept {
|
||||||
|
if (p == nullptr) return nullptr;
|
||||||
|
#if defined(LIBIPC_CPP_20)
|
||||||
|
std::destroy_at(p);
|
||||||
|
#elif defined(LIBIPC_CPP_17)
|
||||||
|
std::destroy(std::begin(*p), std::end(*p));
|
||||||
|
#else
|
||||||
|
for (auto &elem : *p) destroy(std::addressof(elem));
|
||||||
|
#endif
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys a range of objects.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/destroy
|
||||||
|
*/
|
||||||
|
template <typename ForwardIt>
|
||||||
|
void destroy(ForwardIt first, ForwardIt last) noexcept {
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
std::destroy(first, last);
|
||||||
|
#else
|
||||||
|
for (; first != last; ++first) {
|
||||||
|
destroy(std::addressof(*first));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys a number of objects in a range.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/destroy_n
|
||||||
|
*/
|
||||||
|
template <typename ForwardIt, typename Size>
|
||||||
|
ForwardIt destroy_n(ForwardIt first, Size n) noexcept {
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
return std::destroy_n(first, n);
|
||||||
|
#else
|
||||||
|
for (; n > 0; (void) ++first, --n)
|
||||||
|
destroy(std::addressof(*first));
|
||||||
|
return first;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructs objects by default-initialization
|
||||||
|
* in an uninitialized area of memory, defined by a start and a count.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/uninitialized_default_construct_n
|
||||||
|
*/
|
||||||
|
template <typename ForwardIt, typename Size>
|
||||||
|
ForwardIt uninitialized_default_construct_n(ForwardIt first, Size n) {
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
return std::uninitialized_default_construct_n(first, n);
|
||||||
|
#else
|
||||||
|
using T = typename std::iterator_traits<ForwardIt>::value_type;
|
||||||
|
ForwardIt current = first;
|
||||||
|
LIBIPC_TRY {
|
||||||
|
for (; n > 0; (void) ++current, --n)
|
||||||
|
::new (horrible_cast<void *>(std::addressof(*current))) T;
|
||||||
|
return current;
|
||||||
|
} LIBIPC_CATCH(...) {
|
||||||
|
destroy(first, current);
|
||||||
|
LIBIPC_THROW(, first);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Moves a number of objects to an uninitialized area of memory.
|
||||||
|
* \see https://en.cppreference.com/w/cpp/memory/uninitialized_move_n
|
||||||
|
*/
|
||||||
|
template <typename InputIt, typename Size, typename NoThrowForwardIt>
|
||||||
|
auto uninitialized_move_n(InputIt first, Size count, NoThrowForwardIt d_first)
|
||||||
|
-> std::pair<InputIt, NoThrowForwardIt> {
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
return std::uninitialized_move_n(first, count, d_first);
|
||||||
|
#else
|
||||||
|
using Value = typename std::iterator_traits<NoThrowForwardIt>::value_type;
|
||||||
|
NoThrowForwardIt current = d_first;
|
||||||
|
LIBIPC_TRY {
|
||||||
|
for (; count > 0; ++first, (void) ++current, --count) {
|
||||||
|
::new (static_cast<void *>(std::addressof(*current))) Value(std::move(*first));
|
||||||
|
}
|
||||||
|
} LIBIPC_CATCH(...) {
|
||||||
|
destroy(d_first, current);
|
||||||
|
LIBIPC_THROW(, {first, d_first});
|
||||||
|
}
|
||||||
|
return {first, current};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
25
include/libipc/mem/new.h
Normal file
25
include/libipc/mem/new.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* \file libipc/mem.h
|
||||||
|
* \author mutouyun (orz@orzz.org)
|
||||||
|
* \brief Global memory management.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "libipc/imp/aligned.h"
|
||||||
|
#include "libipc/imp/uninitialized.h"
|
||||||
|
#include "libipc/imp/byte.h"
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
|
||||||
|
#include "libipc/export.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
@ -17,6 +17,7 @@ include_directories(
|
|||||||
|
|
||||||
file(GLOB SRC_FILES
|
file(GLOB SRC_FILES
|
||||||
${LIBIPC_PROJECT_DIR}/test/*.cpp
|
${LIBIPC_PROJECT_DIR}/test/*.cpp
|
||||||
|
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
|
||||||
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
||||||
)
|
)
|
||||||
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h)
|
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h)
|
||||||
|
|||||||
57
test/imp/test_imp_byte.cpp
Normal file
57
test/imp/test_imp_byte.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include "libipc/imp/byte.h"
|
||||||
|
#include "libipc/imp/span.h"
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
|
||||||
|
TEST(byte, construct) {
|
||||||
|
{
|
||||||
|
LIBIPC_UNUSED ipc::byte b;
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ipc::byte b{};
|
||||||
|
EXPECT_EQ(int(b), 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ipc::byte b{123};
|
||||||
|
EXPECT_EQ(int(b), 123);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ipc::byte b{65535};
|
||||||
|
EXPECT_EQ(int(b), 255);
|
||||||
|
EXPECT_EQ(std::int8_t(b), -1);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ipc::byte b{65536};
|
||||||
|
EXPECT_EQ(int(b), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(byte, compare) {
|
||||||
|
{
|
||||||
|
ipc::byte b1{}, b2{};
|
||||||
|
EXPECT_EQ(b1, b2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ipc::byte b1{}, b2(321);
|
||||||
|
EXPECT_NE(b1, b2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(byte, byte_cast) {
|
||||||
|
int a = 654321;
|
||||||
|
int *pa = &a;
|
||||||
|
|
||||||
|
// int * => byte *
|
||||||
|
ipc::byte *pb = ipc::byte_cast(pa);
|
||||||
|
EXPECT_EQ((std::size_t)pb, (std::size_t)pa);
|
||||||
|
|
||||||
|
// byte * => int32_t *
|
||||||
|
std::int32_t *pc = ipc::byte_cast<std::int32_t>(pb);
|
||||||
|
EXPECT_EQ(*pc, a);
|
||||||
|
|
||||||
|
// byte alignment check
|
||||||
|
EXPECT_EQ(ipc::byte_cast<int>(pb + 1), nullptr);
|
||||||
|
}
|
||||||
90
test/imp/test_imp_detect_plat.cpp
Normal file
90
test/imp/test_imp_detect_plat.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
|
||||||
|
TEST(detect_plat, os) {
|
||||||
|
#if defined(LIBIPC_OS_WINCE)
|
||||||
|
std::cout << "LIBIPC_OS_WINCE\n";
|
||||||
|
#elif defined(LIBIPC_OS_WIN)
|
||||||
|
std::cout << "LIBIPC_OS_WIN\n";
|
||||||
|
#elif defined(LIBIPC_OS_LINUX)
|
||||||
|
std::cout << "LIBIPC_OS_LINUX\n";
|
||||||
|
#elif defined(LIBIPC_OS_QNX)
|
||||||
|
std::cout << "LIBIPC_OS_QNX\n";
|
||||||
|
#elif defined(LIBIPC_OS_ANDROID)
|
||||||
|
std::cout << "LIBIPC_OS_ANDROID\n";
|
||||||
|
#else
|
||||||
|
ASSERT_TRUE(false);
|
||||||
|
#endif
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(detect_plat, cc) {
|
||||||
|
#if defined(LIBIPC_CC_MSVC)
|
||||||
|
std::cout << "LIBIPC_CC_MSVC\n";
|
||||||
|
#elif defined(LIBIPC_CC_GNUC)
|
||||||
|
std::cout << "LIBIPC_CC_GNUC\n";
|
||||||
|
#else
|
||||||
|
ASSERT_TRUE(false);
|
||||||
|
#endif
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(detect_plat, cpp) {
|
||||||
|
#if defined(LIBIPC_CPP_20)
|
||||||
|
std::cout << "LIBIPC_CPP_20\n";
|
||||||
|
#elif defined(LIBIPC_CPP_17)
|
||||||
|
std::cout << "LIBIPC_CPP_17\n";
|
||||||
|
#elif defined(LIBIPC_CPP_14)
|
||||||
|
std::cout << "LIBIPC_CPP_14\n";
|
||||||
|
#else
|
||||||
|
ASSERT_TRUE(false);
|
||||||
|
#endif
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(detect_plat, byte_order) {
|
||||||
|
auto is_endian_little = [] {
|
||||||
|
union {
|
||||||
|
std::int32_t a;
|
||||||
|
std::int8_t b;
|
||||||
|
} c;
|
||||||
|
c.a = 1;
|
||||||
|
return c.b == 1;
|
||||||
|
};
|
||||||
|
EXPECT_EQ(!!LIBIPC_ENDIAN_LIT, is_endian_little());
|
||||||
|
EXPECT_NE(!!LIBIPC_ENDIAN_BIG, is_endian_little());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(detect_plat, fallthrough) {
|
||||||
|
switch (0) {
|
||||||
|
case 0:
|
||||||
|
std::cout << "fallthrough 0\n";
|
||||||
|
LIBIPC_FALLTHROUGH;
|
||||||
|
case 1:
|
||||||
|
std::cout << "fallthrough 1\n";
|
||||||
|
LIBIPC_FALLTHROUGH;
|
||||||
|
default:
|
||||||
|
std::cout << "fallthrough default\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(detect_plat, unused) {
|
||||||
|
LIBIPC_UNUSED int abc;
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(detect_plat, likely_unlikely) {
|
||||||
|
int xx = sizeof(int);
|
||||||
|
if LIBIPC_LIKELY(xx < sizeof(long long)) {
|
||||||
|
std::cout << "sizeof(int) < sizeof(long long)\n";
|
||||||
|
} else if LIBIPC_UNLIKELY(xx < sizeof(char)) {
|
||||||
|
std::cout << "sizeof(int) < sizeof(char)\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "sizeof(int) < whatever\n";
|
||||||
|
}
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
89
test/imp/test_imp_generic.cpp
Normal file
89
test/imp/test_imp_generic.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include "libipc/imp/generic.h"
|
||||||
|
#include "libipc/imp/detect_plat.h"
|
||||||
|
|
||||||
|
TEST(generic, countof) {
|
||||||
|
struct {
|
||||||
|
constexpr int Size() const noexcept { return 3; }
|
||||||
|
} sv;
|
||||||
|
EXPECT_FALSE(ipc::detail_countof::trait_has_size<decltype(sv)>::value);
|
||||||
|
EXPECT_TRUE (ipc::detail_countof::trait_has_Size<decltype(sv)>::value);
|
||||||
|
|
||||||
|
std::vector<int> vec {1, 2, 3, 4, 5};
|
||||||
|
int arr[] {7, 6, 5, 4, 3, 2, 1};
|
||||||
|
auto il = {9, 7, 6, 4, 3, 1, 5};
|
||||||
|
EXPECT_EQ(ipc::countof(sv) , sv.Size());
|
||||||
|
EXPECT_EQ(ipc::countof(vec), vec.size());
|
||||||
|
EXPECT_EQ(ipc::countof(arr), sizeof(arr) / sizeof(arr[0]));
|
||||||
|
EXPECT_EQ(ipc::countof(il) , il.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(generic, dataof) {
|
||||||
|
struct {
|
||||||
|
int *Data() const noexcept { return (int *)this; }
|
||||||
|
} sv;
|
||||||
|
EXPECT_FALSE(ipc::detail_dataof::trait_has_data<decltype(sv)>::value);
|
||||||
|
EXPECT_TRUE (ipc::detail_dataof::trait_has_Data<decltype(sv)>::value);
|
||||||
|
|
||||||
|
std::vector<int> vec {1, 2, 3, 4, 5};
|
||||||
|
int arr[] {7, 6, 5, 4, 3, 2, 1};
|
||||||
|
auto il = {9, 7, 6, 4, 3, 1, 5};
|
||||||
|
EXPECT_EQ(ipc::dataof(sv) , sv.Data());
|
||||||
|
EXPECT_EQ(ipc::dataof(vec), vec.data());
|
||||||
|
EXPECT_EQ(ipc::dataof(arr), arr);
|
||||||
|
EXPECT_EQ(ipc::dataof(il) , il.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(generic, horrible_cast) {
|
||||||
|
struct A {
|
||||||
|
int a_;
|
||||||
|
} a {123};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
char a_[sizeof(int)];
|
||||||
|
} b = ipc::horrible_cast<B>(a);
|
||||||
|
|
||||||
|
EXPECT_EQ(b.a_[1], 0);
|
||||||
|
EXPECT_EQ(b.a_[2], 0);
|
||||||
|
#if LIBIPC_ENDIAN_LIT
|
||||||
|
EXPECT_EQ(b.a_[0], 123);
|
||||||
|
EXPECT_EQ(b.a_[3], 0);
|
||||||
|
#else
|
||||||
|
EXPECT_EQ(b.a_[3], 123);
|
||||||
|
EXPECT_EQ(b.a_[0], 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBIPC_ENDIAN_LIT
|
||||||
|
EXPECT_EQ(ipc::horrible_cast<std::uint32_t>(0xff00'0000'0001ll), 1);
|
||||||
|
#else
|
||||||
|
EXPECT_EQ(ipc::horrible_cast<std::uint32_t>(0xff00'0000'0001ll), 0xff00);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(LIBIPC_CPP_17)
|
||||||
|
TEST(generic, in_place) {
|
||||||
|
EXPECT_TRUE((std::is_same<std::in_place_t, ipc::in_place_t>::value));
|
||||||
|
[](ipc::in_place_t) {}(std::in_place);
|
||||||
|
[](std::in_place_t) {}(ipc::in_place);
|
||||||
|
}
|
||||||
|
#endif/*LIBIPC_CPP_17*/
|
||||||
|
|
||||||
|
TEST(generic, copy_cvref) {
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int , long>, long >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int & , long>, long & >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int &&, long>, long &&>()));
|
||||||
|
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int const , long>, long const >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int const & , long>, long const & >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int const &&, long>, long const &&>()));
|
||||||
|
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int volatile , long>, long volatile >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int volatile & , long>, long volatile & >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int volatile &&, long>, long volatile &&>()));
|
||||||
|
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int const volatile , long>, long const volatile >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int const volatile & , long>, long const volatile & >()));
|
||||||
|
EXPECT_TRUE((std::is_same<ipc::copy_cvref_t<int const volatile &&, long>, long const volatile &&>()));
|
||||||
|
}
|
||||||
66
test/imp/test_imp_span.cpp
Normal file
66
test/imp/test_imp_span.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include "libipc/imp/span.h"
|
||||||
|
#include "libipc/imp/generic.h"
|
||||||
|
#include "libipc/imp/byte.h"
|
||||||
|
|
||||||
|
TEST(span, to_address) {
|
||||||
|
int *a = new int;
|
||||||
|
EXPECT_EQ(ipc::detail_span::to_address(a), a);
|
||||||
|
std::unique_ptr<int> b {a};
|
||||||
|
EXPECT_EQ(ipc::detail_span::to_address(b), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(span, span) {
|
||||||
|
auto test_proc = [](auto &&buf, auto &&sp) {
|
||||||
|
EXPECT_EQ(ipc::countof(buf), sp.size());
|
||||||
|
EXPECT_EQ(sizeof(buf[0]) * ipc::countof(buf), sp.size_bytes());
|
||||||
|
for (std::size_t i = 0; i < sp.size(); ++i) {
|
||||||
|
EXPECT_EQ(buf[i], sp[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
{
|
||||||
|
int buf[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
auto sp = ipc::make_span(buf);
|
||||||
|
test_proc(buf, sp);
|
||||||
|
test_proc(ipc::make_span({0, 1, 2}) , sp.first(3));
|
||||||
|
test_proc(ipc::make_span({6, 7, 8, 9}), sp.last(4));
|
||||||
|
test_proc(ipc::make_span({3, 4, 5, 6}), sp.subspan(3, 4));
|
||||||
|
test_proc(ipc::make_span({3, 4, 5, 6, 7, 8, 9}), sp.subspan(3));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::vector<int> buf = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
auto sp = ipc::make_span(buf);
|
||||||
|
ipc::span<int> sp2 {buf.begin(), buf.end()};
|
||||||
|
EXPECT_EQ(sp, sp2);
|
||||||
|
test_proc(buf, sp);
|
||||||
|
test_proc(ipc::make_span({0, 1, 2}) , sp.first(3));
|
||||||
|
test_proc(ipc::make_span({6, 7, 8, 9}), sp.last(4));
|
||||||
|
test_proc(ipc::make_span({3, 4, 5, 6}), sp.subspan(3, 4));
|
||||||
|
test_proc(ipc::make_span({3, 4, 5, 6, 7, 8, 9}), sp.subspan(3));
|
||||||
|
test_proc(ipc::make_span((ipc::byte *)sp.data(), sp.size_bytes()), ipc::as_bytes(sp));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::string buf = "0123456789";
|
||||||
|
auto sp = ipc::make_span(buf);
|
||||||
|
// if (sp == ipc::make_span({nullptr})) {}
|
||||||
|
test_proc(buf, sp);
|
||||||
|
test_proc(ipc::make_span("012", 3) , sp.first(3));
|
||||||
|
test_proc(ipc::make_span("6789", 4), sp.last(4));
|
||||||
|
test_proc(ipc::make_span("3456", 4), sp.subspan(3, 4));
|
||||||
|
test_proc(ipc::make_span("3456789", 7), sp.subspan(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(span, construct) {
|
||||||
|
struct test_imp_span_construct {
|
||||||
|
/* data */
|
||||||
|
int size() const noexcept { return sizeof(this); }
|
||||||
|
auto Data() const noexcept { return this; }
|
||||||
|
} d1;
|
||||||
|
ipc::span<test_imp_span_construct const> sp {d1};
|
||||||
|
// ipc::span<int const> spp {d1};
|
||||||
|
EXPECT_EQ(sp.size(), d1.size());
|
||||||
|
EXPECT_EQ(sp.data(), d1.Data());
|
||||||
|
}
|
||||||
46
test/imp/test_imp_uninitialized.cpp
Normal file
46
test/imp/test_imp_uninitialized.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include "libipc/imp/uninitialized.h"
|
||||||
|
|
||||||
|
TEST(uninitialized, construct) {
|
||||||
|
struct Foo {
|
||||||
|
int a_;
|
||||||
|
short b_;
|
||||||
|
char c_;
|
||||||
|
};
|
||||||
|
std::aligned_storage_t<sizeof(Foo)> foo;
|
||||||
|
Foo *pfoo = ipc::construct<Foo>(&foo, 123, short{321}, '1');
|
||||||
|
EXPECT_EQ(pfoo->a_, 123);
|
||||||
|
EXPECT_EQ(pfoo->b_, 321);
|
||||||
|
EXPECT_EQ(pfoo->c_, '1');
|
||||||
|
ipc::destroy(pfoo);
|
||||||
|
|
||||||
|
static int bar_test_flag = 0;
|
||||||
|
struct Bar : Foo {
|
||||||
|
Bar(int a, short b, char c)
|
||||||
|
: Foo{a, b, c} {
|
||||||
|
++bar_test_flag;
|
||||||
|
}
|
||||||
|
~Bar() { --bar_test_flag; }
|
||||||
|
};
|
||||||
|
std::aligned_storage_t<sizeof(Bar)> bar;
|
||||||
|
Bar *pbar = ipc::construct<Bar>(&bar, 123, short(321), '1');
|
||||||
|
EXPECT_EQ(pbar->a_, 123);
|
||||||
|
EXPECT_EQ(pbar->b_, 321);
|
||||||
|
EXPECT_EQ(pbar->c_, '1');
|
||||||
|
EXPECT_EQ(bar_test_flag, 1);
|
||||||
|
ipc::destroy(pbar);
|
||||||
|
EXPECT_EQ(bar_test_flag, 0);
|
||||||
|
|
||||||
|
std::aligned_storage_t<sizeof(Bar)> bars[3];
|
||||||
|
for (auto &b : bars) {
|
||||||
|
auto pb = ipc::construct<Bar>(&b, 321, short(123), '3');
|
||||||
|
EXPECT_EQ(pb->a_, 321);
|
||||||
|
EXPECT_EQ(pb->b_, 123);
|
||||||
|
EXPECT_EQ(pb->c_, '3');
|
||||||
|
}
|
||||||
|
//EXPECT_EQ(bar_test_flag, ipc::countof(bars));
|
||||||
|
ipc::destroy(reinterpret_cast<Bar(*)[3]>(&bars));
|
||||||
|
EXPECT_EQ(bar_test_flag, 0);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user