From 39aed2f41b45e5866dd8039aadb3808fd25659cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sat, 3 Aug 2024 23:14:36 +0200 Subject: [PATCH] offset_ptr improvements: - Use simplier traits to reduce dependencies and add support for nullptr - Add tests cover construction/assignment from literal zero and nullptr --- include/boost/interprocess/offset_ptr.hpp | 132 +++++++++++++++------- test/offset_ptr_test.cpp | 103 ++++++++++++++--- 2 files changed, 179 insertions(+), 56 deletions(-) diff --git a/include/boost/interprocess/offset_ptr.hpp b/include/boost/interprocess/offset_ptr.hpp index 5619ec3..dcae467 100644 --- a/include/boost/interprocess/offset_ptr.hpp +++ b/include/boost/interprocess/offset_ptr.hpp @@ -21,11 +21,7 @@ #include #include - -#include -#include -#include -#include +#include #include #include @@ -33,15 +29,14 @@ #include #include //alignment_of, aligned_storage #include -#include #include +#include #if defined(BOOST_GCC) && (BOOST_GCC >= 40600) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif - //!\file //!Describes a smart pointer that stores the offset between this pointer and //!target pointee, called offset_ptr. @@ -59,15 +54,62 @@ struct has_trivial_destructor; namespace interprocess { #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + typedef decltype(nullptr) op_nullptr_t; +#else +struct op_nullptr_t +{ + void* lx; //to achieve pointer alignment + + struct nat {int bool_conversion;}; + + BOOST_INTERPROCESS_FORCEINLINE op_nullptr_t() {} + + BOOST_INTERPROCESS_FORCEINLINE op_nullptr_t(int nat::*) {} + + BOOST_INTERPROCESS_FORCEINLINE operator int nat::*() const { return 0; } + + template + BOOST_INTERPROCESS_FORCEINLINE operator T*() const { return 0; } + + template + BOOST_INTERPROCESS_FORCEINLINE operator T U::* () const { return 0; } + + friend BOOST_INTERPROCESS_FORCEINLINE bool operator==(op_nullptr_t, op_nullptr_t) { return true; } + friend BOOST_INTERPROCESS_FORCEINLINE bool operator!=(op_nullptr_t, op_nullptr_t) { return false; } +}; + +#endif + namespace ipcdetail { + //workarounds for void offset_ptrs + struct op_nat{}; + + template struct op_reference + : add_reference + {}; + + template <> struct op_reference + { typedef op_nat type; }; + + template <> struct op_reference + { typedef op_nat type; }; + + template <> struct op_reference + { typedef op_nat type; }; + + template <> struct op_reference + { typedef op_nat type; }; + template union offset_ptr_internal { - BOOST_STATIC_ASSERT(sizeof(OffsetType) >= sizeof(uintptr_t)); - BOOST_STATIC_ASSERT(boost::is_integral::value && boost::is_unsigned::value); + BOOST_INTERPROCESS_STATIC_ASSERT(sizeof(OffsetType) >= sizeof(uintptr_t)); + BOOST_INTERPROCESS_STATIC_ASSERT(boost::move_detail::is_integral::value && boost::move_detail::is_unsigned::value); - explicit offset_ptr_internal(OffsetType off) + BOOST_INTERPROCESS_FORCEINLINE explicit offset_ptr_internal(OffsetType off) : m_offset(off) {} @@ -196,14 +238,14 @@ namespace ipcdetail { template struct enable_if_convertible_equal_address - : enable_if_c< ::boost::is_convertible::value + : enable_if_c< ::boost::move_detail::is_convertible::value && offset_ptr_maintains_address::value , Ret> {}; template struct enable_if_convertible_unequal_address - : enable_if_c< ::boost::is_convertible::value + : enable_if_c< ::boost::move_detail::is_convertible::value && !offset_ptr_maintains_address::value , Ret> {}; @@ -245,7 +287,8 @@ class offset_ptr typedef PointedType element_type; typedef PointedType * pointer; typedef typename ipcdetail:: - add_reference::type reference; + op_reference::type reference; + typedef typename ipcdetail:: remove_volatile::type @@ -262,17 +305,25 @@ class offset_ptr : internal(1) {} - //!Constructor from raw pointer (allows "0" pointer conversion). + //!Constructor from nullptr. //!Never throws. - BOOST_INTERPROCESS_FORCEINLINE offset_ptr(pointer ptr) BOOST_NOEXCEPT - : internal(ipcdetail::offset_ptr_to_offset(ptr, this)) + BOOST_INTERPROCESS_FORCEINLINE offset_ptr(op_nullptr_t) BOOST_NOEXCEPT + : internal(1) {} + #if defined( BOOST_NO_CXX11_NULLPTR ) + //!Constructor from nullptr. Some compilers (e.g. g++14) in C++03 mode have problems with op_nullptr_t + //!so a helper overload is needed. Never throws. + BOOST_INTERPROCESS_FORCEINLINE offset_ptr(int ipcdetail::op_nat::*) BOOST_NOEXCEPT + : internal(1) + {} + #endif //BOOST_NO_CXX11_NULLPTR + //!Constructor from other pointer. //!Never throws. template BOOST_INTERPROCESS_FORCEINLINE offset_ptr( T *ptr - , typename ipcdetail::enable_if< ::boost::is_convertible >::type * = 0) BOOST_NOEXCEPT + , typename ipcdetail::enable_if< ::boost::move_detail::is_convertible >::type * = 0) BOOST_NOEXCEPT : internal(ipcdetail::offset_ptr_to_offset(static_cast(ptr), this)) {} @@ -303,19 +354,6 @@ class offset_ptr #endif - //!Constructor from other offset_ptr. Only takes part in overload resolution - //!if PointedType* is constructible from T2* other than via a conversion (e.g. cast to a derived class). Never throws. - template - BOOST_INTERPROCESS_FORCEINLINE explicit offset_ptr(const offset_ptr &ptr - #ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED - , typename ipcdetail::enable_if_c< - !::boost::is_convertible::value && ::boost::is_constructible::value - >::type * = 0 - #endif - ) BOOST_NOEXCEPT - : internal(ipcdetail::offset_ptr_to_offset(static_cast(ptr.get()), this)) - {} - //!Emulates static_cast operator. //!Never throws. template @@ -359,7 +397,7 @@ class offset_ptr //!Dereferencing operator, if it is a null offset_ptr behavior //! is undefined. Never throws. - BOOST_INTERPROCESS_FORCEINLINE reference operator* () const BOOST_NOEXCEPT + BOOST_INTERPROCESS_FORCEINLINE reference operator*() const BOOST_NOEXCEPT { pointer p = this->get(); reference r = *p; @@ -371,14 +409,6 @@ class offset_ptr BOOST_INTERPROCESS_FORCEINLINE reference operator[](difference_type idx) const BOOST_NOEXCEPT { return this->get()[idx]; } - //!Assignment from pointer (saves extra conversion). - //!Never throws. - BOOST_INTERPROCESS_FORCEINLINE offset_ptr& operator= (pointer from) BOOST_NOEXCEPT - { - this->internal.m_offset = ipcdetail::offset_ptr_to_offset(from, this); - return *this; - } - //!Assignment from other offset_ptr. //!Never throws. BOOST_INTERPROCESS_FORCEINLINE offset_ptr& operator= (const offset_ptr & ptr) BOOST_NOEXCEPT @@ -387,12 +417,30 @@ class offset_ptr return *this; } + //!Assignment from nullptr. + //!Never throws. + BOOST_INTERPROCESS_FORCEINLINE offset_ptr& operator= (op_nullptr_t) BOOST_NOEXCEPT + { + this->internal.m_offset = 1; + return *this; + } + + #if defined( BOOST_NO_CXX11_NULLPTR ) + //!Assignment from nullptr. Some compilers (e.g. g++14) in C++03 mode have problems with op_nullptr_t + //!so a helper overload is needed. Never throws. + BOOST_INTERPROCESS_FORCEINLINE offset_ptr& operator= (int ipcdetail::op_nat::*) BOOST_NOEXCEPT + { + this->internal.m_offset = 1; + return *this; + } + #endif //BOOST_NO_CXX11_NULLPTR + //!Assignment from related offset_ptr. If pointers of pointee types //! are assignable, offset_ptrs will be assignable. Never throws. template BOOST_INTERPROCESS_FORCEINLINE #ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED typename ipcdetail::enable_if_c - < ::boost::is_convertible::value, offset_ptr&>::type + < ::boost::move_detail::is_convertible::value, offset_ptr&>::type #else offset_ptr& #endif @@ -473,7 +521,7 @@ class offset_ptr //!Compatibility with pointer_traits //! - BOOST_INTERPROCESS_FORCEINLINE static offset_ptr pointer_to(reference r) BOOST_NOEXCEPT + BOOST_INTERPROCESS_FORCEINLINE static offset_ptr pointer_to(typename ipcdetail::op_reference::type r) BOOST_NOEXCEPT { return offset_ptr(&r); } //!difference_type + offset_ptr @@ -714,7 +762,7 @@ struct pointer_plus_bits, NumBits> //Bits are stored in the lower bits of the pointer except the LSB, //because this bit is used to represent the null pointer. static const O Mask = ((static_cast(1) << NumBits) - static_cast(1)) << 1; - BOOST_STATIC_ASSERT(0 ==(Mask&1)); + BOOST_INTERPROCESS_STATIC_ASSERT(0 ==(Mask&1)); //We must ALWAYS take argument "n" by reference as a copy of a null pointer //with a bit (e.g. offset == 3) would be incorrectly copied and interpreted as non-null. diff --git a/test/offset_ptr_test.cpp b/test/offset_ptr_test.cpp index 8e89ac7..817fefd 100644 --- a/test/offset_ptr_test.cpp +++ b/test/offset_ptr_test.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include using namespace boost::interprocess; @@ -34,15 +33,15 @@ void test_types_and_conversions() typedef offset_ptr pvint_t; typedef offset_ptr pcvint_t; - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); int dummy_int = 9; { pint_t pint(&dummy_int); @@ -71,6 +70,39 @@ void test_types_and_conversions() pvint_t pvint(0); pcvint_t pcvint(0); + { + pint_t pint2 = 0; + pcint_t pcint2 = 0; + pvint_t pvint2 = 0; + pcvint_t pcvint2 = 0; + (void)pint2; + (void)pcint2; + (void)pvint2; + (void)pcvint2; + } + + { + pint_t pint2 = op_nullptr_t(); + pcint_t pcint2 = op_nullptr_t(); + pvint_t pvint2 = op_nullptr_t(); + pcvint_t pcvint2 = op_nullptr_t(); + (void)pint2; + (void)pcint2; + (void)pvint2; + (void)pcvint2; + } + + { + pint_t pint2((op_nullptr_t())); + pcint_t pcint2((op_nullptr_t())); + pvint_t pvint2((op_nullptr_t())); + pcvint_t pcvint2((op_nullptr_t())); + (void)pint2; + (void)pcint2; + (void)pvint2; + (void)pcvint2; + } + pint = &dummy_int; pcint = &dummy_int; pvint = &dummy_int; @@ -108,6 +140,47 @@ void test_types_and_conversions() BOOST_TEST( (pcint - pint) == 0); BOOST_TEST( (pint - pcint) == 0); + + typedef offset_ptr pvoid_t; + typedef offset_ptr pcvoid_t; + typedef offset_ptr pvvoid_t; + typedef offset_ptr pcvvoid_t; + { + pvoid_t pvoid = pint; + pcvoid_t pcvoid = pcint; + pvvoid_t pvvoid = pvint; + pcvvoid_t pcvvoid = pcvint; + (void)pvoid; + (void)pcvoid; + (void)pvvoid; + (void)pcvvoid; + } + { + pvoid_t pvoid(pint); + pcvoid_t pcvoid(pcint); + pvvoid_t pvvoid(pvint); + pcvvoid_t pcvvoid(pcvint); + (void)pvoid; + (void)pcvoid; + (void)pvvoid; + (void)pcvvoid; + } + { + pvoid_t pvoid; + pcvoid_t pcvoid; + pvvoid_t pvvoid; + pcvvoid_t pcvvoid; + + pvoid = pint; + pcvoid = pcint; + pvvoid = pvint; + pcvvoid = pcvint; + + (void)pvoid; + (void)pcvoid; + (void)pvvoid; + (void)pcvvoid; + } } template @@ -116,6 +189,8 @@ void test_base_derived_impl() typename DerivedPtr::element_type d; DerivedPtr pderi(&d); + { BasePtr pbase2 = pderi; (void)pbase2; } + BasePtr pbase(pderi); pbase = pderi; BOOST_TEST(pbase == pderi); @@ -234,10 +309,10 @@ bool test_pointer_traits() { typedef offset_ptr OInt; typedef boost::intrusive::pointer_traits< OInt > PTOInt; - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::value)); - BOOST_STATIC_ASSERT((ipcdetail::is_same::type, offset_ptr >::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::value)); + BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same::type, offset_ptr >::value)); int dummy; OInt oi(&dummy); if(boost::intrusive::pointer_traits::pointer_to(dummy) != oi){ @@ -253,7 +328,7 @@ struct node void test_pointer_plus_bits() { - BOOST_STATIC_ASSERT((boost::intrusive::max_pointer_plus_bits< offset_ptr, boost::move_detail::alignment_of::value >::value >= 1U)); + BOOST_INTERPROCESS_STATIC_ASSERT((boost::intrusive::max_pointer_plus_bits< offset_ptr, boost::move_detail::alignment_of::value >::value >= 1U)); typedef boost::intrusive::pointer_plus_bits< offset_ptr, 1u > ptr_plus_bits; node n, n2;