[libcxx-commits] [PATCH] D132149: [String] Allow fancy pointer as pointer type of basic_string allocator

Brendan Emery via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 21 00:53:50 PST 2022


bemeryesr added a comment.

Hi @philnik. When you get a chance, could you please have a look at my PR? I made a few changes as mentioned in my comment a while ago. I also updated all the unit tests to also use a fancy pointer allocator. Thanks!

In D132149#3799799 <https://reviews.llvm.org/D132149#3799799>, @bemeryesr wrote:

> Thanks for the feedback! Regarding your OffsetPtr example:
>
> 1. My initial code was broken as it was calling the copy assignment operator of `__long` even if `__rep` was a short string. For an offset pointer, the copy assignment operator recalculates the offset to the pointed-to address and so the copied value will be different to the original. Hence, it failed for short strings. So now I perform the copy depending on whether it's a short or a long string.
> 2. As I think you alluded to, it seems that some of the pointer arithmetic is optimized-away by the compiler, leading to incorrect behaviour. In boost::interprocess::offset_ptr they mentioned this: "Note: using the address of a local variable to point to another address is not standard conforming and this can be optimized-away by the compiler. Non-inlining is a method to remain illegal but correct." To deal with this, we used a couple of tricks that prevent the functions from being inlined and the test passes. Would it make sense to add this code as a unit test as is? Or perhaps we should find a better way to deal with the UB? This is the code that works for me:
>
>
>
>   // main.cpp
>   //===----------------------------------------------------------------------===//
>   //
>   // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>   // See https://llvm.org/LICENSE.txt for license information.
>   // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>   //
>   //===----------------------------------------------------------------------===//
>   
>   #include <cassert>
>   #include <string>
>   
>   #include "OffsetPtr.h"
>   
>   int main()
>   {
>       using string = std::basic_string<char, std::char_traits<char>, OffsetPtrAllocator<char>>;
>       {
>           string str3 = "almost long string";
>           string str4 = std::move(str3);
>           assert(str4 == "almost long string");
>       }
>   }
>
>
>
>   // OffsetPtr.h
>   #ifndef OFFSETPTR_H
>   #define OFFSETPTR_H
>   
>   #include <cassert>
>   #include <cstdint>
>   #include <iostream>
>   #include <iterator>
>   #include <type_traits>
>   #include <utility>
>   
>   namespace detail
>   {
>   using local_difference_type = std::ptrdiff_t;
>   
>   local_difference_type SubtractPointersBytesImpl(const volatile void* ptr1, const volatile void* ptr2);
>   
>   // Alias for checking if template types are of the same type after stripping const/volatile qualifiers.
>   template <typename T1, typename T2>
>   using cv_stripped_types_equal =
>       typename std::is_same<typename std::remove_cv<T1>::type, typename std::remove_cv<T2>::type>;
>   
>   template <typename T1, typename T2, typename = std::enable_if_t<cv_stripped_types_equal<T1, T2>::value>>
>   constexpr local_difference_type SubtractPointersBytes(T1* const ptr1, T2* const ptr2)
>   {
>       return SubtractPointersBytesImpl(ptr1, ptr2);
>   }
>   
>   template <typename T>
>   constexpr const T* __attribute__((noinline)) AddOffsetToPointer(const T* const ptr, local_difference_type offset)
>   {
>       return reinterpret_cast<const T*>(reinterpret_cast<const std::uint8_t*>(ptr) + offset);
>   }
>   
>   template <typename T>
>   constexpr const T* __attribute__((noinline)) AddOffsetToPointer(T* const ptr, local_difference_type offset)
>   {
>       return reinterpret_cast<T*>(reinterpret_cast<std::uint8_t*>(ptr) + offset);
>   }
>   
>   }  // namespace detail
>   
>   template <class T>
>   class alignas(std::max(alignof(T), alignof(std::ptrdiff_t))) OffsetPtr
>   {
>       std::ptrdiff_t offset;
>   
>     public:
>       using value_type = T;
>       using iterator_category = std::random_access_iterator_tag;
>       using difference_type = detail::local_difference_type;
>       using pointer = OffsetPtr;
>       using reference = T&;
>   
>       constexpr OffsetPtr() = default;
>       constexpr OffsetPtr(const OffsetPtr& other)
>           : offset(detail::SubtractPointersBytes(other.operator->(), reinterpret_cast<T*>(this)))
>       {
>       }
>   
>       constexpr operator OffsetPtr<const T>() const
>       {
>           return OffsetPtr<const T>{detail::AddOffsetToPointer(reinterpret_cast<const T*>(this), offset)};
>       }
>   
>       constexpr OffsetPtr& operator=(const OffsetPtr& other)
>       {
>           T* ptr = other.operator->();
>           offset = detail::SubtractPointersBytes(ptr, reinterpret_cast<T*>(this));
>           return *this;
>       }
>   
>       constexpr explicit OffsetPtr(T* ptr) : offset(detail::SubtractPointersBytes(ptr, reinterpret_cast<T*>(this))) {}
>   
>       constexpr T* operator->() const
>       {
>           return const_cast<T*>(detail::AddOffsetToPointer(reinterpret_cast<T*>(const_cast<OffsetPtr*>(this)), offset));
>       }
>   
>       constexpr T& operator[](size_t i) { return operator->()[i]; }
>   
>       static OffsetPtr pointer_to(T& e) { return OffsetPtr(&e); }
>   
>       friend OffsetPtr operator+(const OffsetPtr& lhs, size_t rhs)
>       {
>           return OffsetPtr{detail::AddOffsetToPointer(lhs.operator->(), rhs)};
>       }
>   };
>   
>   static_assert(sizeof(OffsetPtr<int>) == 8, "");
>   
>   template <class T>
>   class OffsetPtrAllocator
>   {
>     public:
>       using value_type = T;
>       using pointer = OffsetPtr<T>;
>   
>       pointer allocate(size_t n) { return pointer(std::allocator<T>{}.allocate(n)); }
>   
>       void deallocate(pointer ptr, size_t n) { std::allocator<T>{}.deallocate(ptr.operator->(), n); }
>   };
>   
>   #endif  // OFFSETPTR_H
>
>   // OffsetPtr.cpp
>   #include "OffsetPtr.h"
>   
>   namespace detail
>   {
>   
>   local_difference_type SubtractPointersBytesImpl(const volatile void* const ptr1, const volatile void* const ptr2)
>   {
>       return local_difference_type(reinterpret_cast<local_difference_type>(ptr1) -
>                                    reinterpret_cast<local_difference_type>(ptr2));
>   }
>   
>   }  // namespace detail




Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132149/new/

https://reviews.llvm.org/D132149



More information about the libcxx-commits mailing list