offset_ptr improvements:

- Use simplier traits to reduce dependencies and add support for nullptr
- Add tests cover construction/assignment from literal zero and nullptr
This commit is contained in:
Ion Gaztañaga
2024-08-03 23:14:36 +02:00
parent c886f911c9
commit 39aed2f41b
2 changed files with 179 additions and 56 deletions

View File

@@ -21,11 +21,7 @@
#include <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/detail/workaround.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_constructible.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_unsigned.hpp>
#include <boost/move/detail/type_traits.hpp>
#include <boost/interprocess/interprocess_fwd.hpp>
#include <boost/interprocess/detail/utilities.hpp>
@@ -33,15 +29,14 @@
#include <boost/interprocess/detail/mpl.hpp>
#include <boost/container/detail/type_traits.hpp> //alignment_of, aligned_storage
#include <boost/assert.hpp>
#include <boost/static_assert.hpp>
#include <iosfwd>
#include <cstddef>
#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 <class T>
BOOST_INTERPROCESS_FORCEINLINE operator T*() const { return 0; }
template <class T, class U>
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 <class T> struct op_reference
: add_reference<T>
{};
template <> struct op_reference<void>
{ typedef op_nat type; };
template <> struct op_reference<void const>
{ typedef op_nat type; };
template <> struct op_reference<void volatile>
{ typedef op_nat type; };
template <> struct op_reference<void const volatile>
{ typedef op_nat type; };
template<class OffsetType, std::size_t OffsetAlignment>
union offset_ptr_internal
{
BOOST_STATIC_ASSERT(sizeof(OffsetType) >= sizeof(uintptr_t));
BOOST_STATIC_ASSERT(boost::is_integral<OffsetType>::value && boost::is_unsigned<OffsetType>::value);
BOOST_INTERPROCESS_STATIC_ASSERT(sizeof(OffsetType) >= sizeof(uintptr_t));
BOOST_INTERPROCESS_STATIC_ASSERT(boost::move_detail::is_integral<OffsetType>::value && boost::move_detail::is_unsigned<OffsetType>::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<class From, class To, class Ret = void>
struct enable_if_convertible_equal_address
: enable_if_c< ::boost::is_convertible<From*, To*>::value
: enable_if_c< ::boost::move_detail::is_convertible<From*, To*>::value
&& offset_ptr_maintains_address<From, To>::value
, Ret>
{};
template<class From, class To, class Ret = void>
struct enable_if_convertible_unequal_address
: enable_if_c< ::boost::is_convertible<From*, To*>::value
: enable_if_c< ::boost::move_detail::is_convertible<From*, To*>::value
&& !offset_ptr_maintains_address<From, To>::value
, Ret>
{};
@@ -245,7 +287,8 @@ class offset_ptr
typedef PointedType element_type;
typedef PointedType * pointer;
typedef typename ipcdetail::
add_reference<PointedType>::type reference;
op_reference<PointedType>::type reference;
typedef typename ipcdetail::
remove_volatile<typename ipcdetail::
remove_const<PointedType>::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<OffsetType>(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 <class T>
BOOST_INTERPROCESS_FORCEINLINE offset_ptr( T *ptr
, typename ipcdetail::enable_if< ::boost::is_convertible<T*, PointedType*> >::type * = 0) BOOST_NOEXCEPT
, typename ipcdetail::enable_if< ::boost::move_detail::is_convertible<T*, PointedType*> >::type * = 0) BOOST_NOEXCEPT
: internal(ipcdetail::offset_ptr_to_offset<OffsetType>(static_cast<PointedType*>(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<class T2>
BOOST_INTERPROCESS_FORCEINLINE explicit offset_ptr(const offset_ptr<T2, DifferenceType, OffsetType, OffsetAlignment> &ptr
#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
, typename ipcdetail::enable_if_c<
!::boost::is_convertible<T2*, PointedType*>::value && ::boost::is_constructible<T2*, PointedType*>::value
>::type * = 0
#endif
) BOOST_NOEXCEPT
: internal(ipcdetail::offset_ptr_to_offset<OffsetType>(static_cast<PointedType*>(ptr.get()), this))
{}
//!Emulates static_cast operator.
//!Never throws.
template<class T2, class P2, class O2, std::size_t A2>
@@ -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<OffsetType>(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<class T2> BOOST_INTERPROCESS_FORCEINLINE
#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
typename ipcdetail::enable_if_c
< ::boost::is_convertible<T2*, PointedType*>::value, offset_ptr&>::type
< ::boost::move_detail::is_convertible<T2*, PointedType*>::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<PointedType>::type r) BOOST_NOEXCEPT
{ return offset_ptr(&r); }
//!difference_type + offset_ptr
@@ -714,7 +762,7 @@ struct pointer_plus_bits<boost::interprocess::offset_ptr<T, P, O, A>, 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<O>(1) << NumBits) - static_cast<O>(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.

View File

@@ -11,7 +11,6 @@
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/detail/type_traits.hpp>
#include <boost/intrusive/pointer_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/core/lightweight_test.hpp>
using namespace boost::interprocess;
@@ -34,15 +33,15 @@ void test_types_and_conversions()
typedef offset_ptr<volatile int> pvint_t;
typedef offset_ptr<const volatile int> pcvint_t;
BOOST_STATIC_ASSERT((ipcdetail::is_same<pint_t::element_type, int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pcint_t::element_type, const int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pvint_t::element_type, volatile int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pcvint_t::element_type, const volatile int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pint_t::element_type, int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pcint_t::element_type, const int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pvint_t::element_type, volatile int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pcvint_t::element_type, const volatile int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pint_t::value_type, int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pcint_t::value_type, int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pvint_t::value_type, int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<pcvint_t::value_type, int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pint_t::value_type, int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pcint_t::value_type, int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pvint_t::value_type, int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<pcvint_t::value_type, int>::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<void> pvoid_t;
typedef offset_ptr<const void> pcvoid_t;
typedef offset_ptr<volatile void> pvvoid_t;
typedef offset_ptr<const volatile void> 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<class BasePtr, class DerivedPtr>
@@ -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<int> OInt;
typedef boost::intrusive::pointer_traits< OInt > PTOInt;
BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::element_type, int>::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::pointer, OInt >::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::difference_type, OInt::difference_type >::value));
BOOST_STATIC_ASSERT((ipcdetail::is_same<PTOInt::rebind_pointer<double>::type, offset_ptr<double> >::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<PTOInt::element_type, int>::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<PTOInt::pointer, OInt >::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<PTOInt::difference_type, OInt::difference_type >::value));
BOOST_INTERPROCESS_STATIC_ASSERT((ipcdetail::is_same<PTOInt::rebind_pointer<double>::type, offset_ptr<double> >::value));
int dummy;
OInt oi(&dummy);
if(boost::intrusive::pointer_traits<OInt>::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<void>, boost::move_detail::alignment_of<node>::value >::value >= 1U));
BOOST_INTERPROCESS_STATIC_ASSERT((boost::intrusive::max_pointer_plus_bits< offset_ptr<void>, boost::move_detail::alignment_of<node>::value >::value >= 1U));
typedef boost::intrusive::pointer_plus_bits< offset_ptr<node>, 1u > ptr_plus_bits;
node n, n2;