Add imp for subsequent refactoring

This commit is contained in:
mutouyun 2025-01-02 18:21:35 +08:00 committed by 木头云
parent f8e71a548c
commit 8098c8e37a
13 changed files with 1564 additions and 0 deletions

View 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
View 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

View 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

View 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
View 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

View 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
View 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

View File

@ -18,6 +18,8 @@ include_directories(
# Collect only new test files (exclude archive directory)
file(GLOB SRC_FILES
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
)
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)

View 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);
}

View 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();
}

View 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 &&>()));
}

View 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());
}

View 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);
}