From 67776aea6579f54a137c22abd3b7bd6ef46e955b Mon Sep 17 00:00:00 2001 From: mutouyun Date: Thu, 2 Jan 2025 18:21:35 +0800 Subject: [PATCH 01/34] Add imp for subsequent refactoring --- include/libipc/imp/aligned.h | 74 +++++++ include/libipc/imp/byte.h | 163 +++++++++++++++ include/libipc/imp/detect_plat.h | 226 +++++++++++++++++++++ include/libipc/imp/generic.h | 299 ++++++++++++++++++++++++++++ include/libipc/imp/span.h | 281 ++++++++++++++++++++++++++ include/libipc/imp/uninitialized.h | 146 ++++++++++++++ include/libipc/mem/new.h | 25 +++ test/CMakeLists.txt | 1 + test/imp/test_imp_byte.cpp | 57 ++++++ test/imp/test_imp_detect_plat.cpp | 90 +++++++++ test/imp/test_imp_generic.cpp | 89 +++++++++ test/imp/test_imp_span.cpp | 66 ++++++ test/imp/test_imp_uninitialized.cpp | 46 +++++ 13 files changed, 1563 insertions(+) create mode 100644 include/libipc/imp/aligned.h create mode 100644 include/libipc/imp/byte.h create mode 100644 include/libipc/imp/detect_plat.h create mode 100644 include/libipc/imp/generic.h create mode 100644 include/libipc/imp/span.h create mode 100644 include/libipc/imp/uninitialized.h create mode 100644 include/libipc/mem/new.h create mode 100644 test/imp/test_imp_byte.cpp create mode 100644 test/imp/test_imp_detect_plat.cpp create mode 100644 test/imp/test_imp_generic.cpp create mode 100644 test/imp/test_imp_span.cpp create mode 100644 test/imp/test_imp_uninitialized.cpp diff --git a/include/libipc/imp/aligned.h b/include/libipc/imp/aligned.h new file mode 100644 index 00000000..e3da1671 --- /dev/null +++ b/include/libipc/imp/aligned.h @@ -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 +#include + +#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 +class aligned { + alignas(AlignT) std::array storage_; + +public: + /** + * \brief Returns a pointer to the aligned storage. + * \return A pointer to the aligned storage. + */ + T *ptr() noexcept { + return reinterpret_cast(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(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 +constexpr T round_up(T value, T alignment) noexcept { + return (value + alignment - 1) & ~(alignment - 1); +} + +} // namespace ipc diff --git a/include/libipc/imp/byte.h b/include/libipc/imp/byte.h new file mode 100644 index 00000000..69da411a --- /dev/null +++ b/include/libipc/imp/byte.h @@ -0,0 +1,163 @@ +/** + * \file libipc/byte.h + * \author mutouyun (orz@orzz.org) + * \brief Define the byte type. + */ +#pragma once + +#include +#include +#include // 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 +using is_integral = + typename std::enable_if::value>::type; + +template +using is_not_byte = + typename std::enable_if::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 > + constexpr byte(T v) noexcept + : bits_(static_cast(v)) {} + +#ifdef LIBIPC_CPP_LIB_BYTE_ + constexpr byte(std::byte b) noexcept + : byte(std::to_integer(b)) {} +#endif // LIBIPC_CPP_LIB_BYTE_ + + template > + constexpr operator T() const noexcept { + return static_cast(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 > +constexpr T to_integer(byte b) noexcept { + return T(b); +} + +/// \brief std::operator<<, operator>> + +template > +constexpr byte operator<<(byte b, T shift) noexcept { + return byte(to_integer(b) << shift); +} + +template > +constexpr byte operator>>(byte b, T shift) noexcept { + return byte(to_integer(b) >> shift); +} + +/// \brief std::operator<<=, operator>>= + +template > +constexpr byte &operator<<=(byte &b, T shift) noexcept { + return b = b << shift; +} + +template > +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(l) | to_integer(r)); } +constexpr byte operator&(byte l, byte r) noexcept { return byte(to_integer(l) & to_integer(r)); } +constexpr byte operator^(byte l, byte r) noexcept { return byte(to_integer(l) ^ to_integer(r)); } +constexpr byte operator~(byte b) noexcept { return byte(~to_integer(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 > +byte *byte_cast(T *p) noexcept { + return reinterpret_cast(p); +} + +template > +byte const *byte_cast(T const *p) noexcept { + return reinterpret_cast(p); +} + +/// \brief Cast byte* to a pointer of another type. + +template > +T *byte_cast(byte *p) noexcept { + if (reinterpret_cast(p) % alignof(T) != 0) { + return nullptr; + } + return reinterpret_cast(p); +} + +template ::type, + typename = detail_byte::is_not_byte> +U *byte_cast(byte const *p) noexcept { + if (reinterpret_cast(p) % alignof(T) != 0) { + return nullptr; + } + return reinterpret_cast(p); +} + +/// \brief Converts a span into a view of its underlying bytes. +/// \see https://en.cppreference.com/w/cpp/container/span/as_bytes + +template ::value, byte const, byte>::type> +auto as_bytes(span s) noexcept -> span { + return {byte_cast(s.data()), s.size_bytes()}; +} + +} // namespace ipc diff --git a/include/libipc/imp/detect_plat.h b/include/libipc/imp/detect_plat.h new file mode 100644 index 00000000..6095cec9 --- /dev/null +++ b/include/libipc/imp/detect_plat.h @@ -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 diff --git a/include/libipc/imp/generic.h b/include/libipc/imp/generic.h new file mode 100644 index 00000000..e31e4043 --- /dev/null +++ b/include/libipc/imp/generic.h @@ -0,0 +1,299 @@ +/** + * \file libipc/generic.h + * \author mutouyun (orz@orzz.org) + * \brief Tools for generic programming. + */ +#pragma once + +#include +#include // std::declval, std::true_type, std::false_type +#include // 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 +using void_t = void; + +/** + * \brief A type-list for generic programming. +*/ +template +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 + constexpr auto operator()(T tag, A &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(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 +struct is_same_first : std::false_type {}; + +template +struct is_same_first : std::true_type {}; + +} // namespace detail + +template +using not_match = + typename std::enable_if::type...>::value, bool>::type; + +/** + * \brief Determines whether a type is specialized from a particular template. + */ +template