123789456ye

已AFO

Proxy 库解析(三)

前言

本篇的内容对理解 Proxy 如何构造和调用关系不大
主要内容是 Proxy 自己写的内存布局,可以结合提案理解

ptrs

template <class F>
struct converter {
  explicit converter(F f) noexcept : f_(std::move(f)) {}
  converter(const converter&) = delete;
  template <class T>
  operator T() && noexcept(
      std::is_nothrow_invocable_r_v<T, F, std::in_place_type_t<T>>)
    requires(std::is_invocable_r_v<T, F, std::in_place_type_t<T>>)
  {
    return std::move(f_)(std::in_place_type<T>);
  }

private:
  F f_;
};

隐式转换到 T
注意有一个 && 限定,因此只有 converter&& 类型才能调用

template <class LR, class CLR, class RR, class CRR>
class observer_ptr {
public:
  explicit observer_ptr(LR lr) : lr_(lr) {}
  observer_ptr(const observer_ptr&) = default;
  auto operator->() noexcept { return std::addressof(lr_); }
  auto operator->() const noexcept {
    return std::addressof(static_cast<CLR>(lr_));
  }
  LR operator*() & noexcept { return static_cast<LR>(lr_); }
  CLR operator*() const& noexcept { return static_cast<CLR>(lr_); }
  RR operator*() && noexcept { return static_cast<RR>(lr_); }
  CRR operator*() const&& noexcept { return static_cast<CRR>(lr_); }

private:
  LR lr_;
};

相当抽象的重载

template <class T, class Alloc, class... Args>
T* allocate(const Alloc& alloc, Args&&... args) {
  auto al =
      typename std::allocator_traits<Alloc>::template rebind_alloc<T>(alloc);
  auto deleter = [&](T* ptr) { al.deallocate(ptr, 1); };
  std::unique_ptr<T, decltype(deleter)> result{al.allocate(1), deleter};
  std::construct_at(result.get(), std::forward<Args>(args)...);
  return result.release();
}
template <class Alloc, class T>
void deallocate(const Alloc& alloc, T* ptr) {
  auto al =
      typename std::allocator_traits<Alloc>::template rebind_alloc<T>(alloc);
  std::destroy_at(ptr);
  al.deallocate(ptr, 1);
}
template <class Alloc>
struct alloc_aware {
  explicit alloc_aware(const Alloc& alloc) noexcept : alloc(alloc) {}
  alloc_aware(const alloc_aware&) noexcept = default;

  [[PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE]]
  Alloc alloc;
};
template <class T>
class indirect_ptr {
public:
  explicit indirect_ptr(T* ptr) noexcept : ptr_(ptr) {}
  auto operator->() noexcept { return std::addressof(**ptr_); }
  auto operator->() const noexcept { return std::addressof(**ptr_); }
  decltype(auto) operator*() & noexcept { return **ptr_; }
  decltype(auto) operator*() const& noexcept { return *std::as_const(*ptr_); }
  decltype(auto) operator*() && noexcept { return *std::move(*ptr_); }
  decltype(auto) operator*() const&& noexcept {
    return *std::move(std::as_const(*ptr_));
  }

protected:
  T* ptr_;
};

template <class T, class Alloc>
class PRO4D_ENFORCE_EBO allocated_ptr PROD_TR_IF_ELIGIBLE
    : private alloc_aware<Alloc>,
      public indirect_ptr<inplace_ptr<T>> {
public:
  template <class... Args>
  allocated_ptr(const Alloc& alloc, Args&&... args)
      : alloc_aware<Alloc>(alloc),
        indirect_ptr<inplace_ptr<T>>(allocate<inplace_ptr<T>>(
            this->alloc, std::in_place, std::forward<Args>(args)...)) {}
  allocated_ptr(const allocated_ptr& rhs)
    requires(std::is_copy_constructible_v<T>)
      : alloc_aware<Alloc>(rhs),
        indirect_ptr<inplace_ptr<T>>(
            allocate<inplace_ptr<T>>(this->alloc, std::in_place, *rhs)) {}
  allocated_ptr(allocated_ptr&& rhs) = delete;
  ~allocated_ptr() noexcept(std::is_nothrow_destructible_v<T>) {
    deallocate(this->alloc, this->ptr_);
  }
};

看起来像是手写了一个内存池

template <class T, class Alloc>
struct PRO4D_ENFORCE_EBO compact_ptr_storage : alloc_aware<Alloc>,
                                               inplace_ptr<T> {
  template <class... Args>
  explicit compact_ptr_storage(const Alloc& alloc, Args&&... args)
      : alloc_aware<Alloc>(alloc),
        inplace_ptr<T>(std::in_place, std::forward<Args>(args)...) {}
};
template <class T, class Alloc>
class compact_ptr PROD_TR_IF_ELIGIBLE
    : public indirect_ptr<compact_ptr_storage<T, Alloc>> {
  using Storage = compact_ptr_storage<T, Alloc>;

public:
  template <class... Args>
  compact_ptr(const Alloc& alloc, Args&&... args)
      : indirect_ptr<Storage>(
            allocate<Storage>(alloc, alloc, std::forward<Args>(args)...)) {}
  compact_ptr(const compact_ptr& rhs)
    requires(std::is_copy_constructible_v<T>)
      : indirect_ptr<Storage>(
            allocate<Storage>(rhs.ptr_->alloc, rhs.ptr_->alloc, *rhs)) {}
  compact_ptr(compact_ptr&& rhs) = delete;
  ~compact_ptr() noexcept(std::is_nothrow_destructible_v<T>) {
    deallocate(this->ptr_->alloc, this->ptr_);
  }
};

struct shared_compact_ptr_storage_base {
  std::atomic_long ref_count = 1;
};
template <class T, class Alloc>
struct PRO4D_ENFORCE_EBO shared_compact_ptr_storage
    : shared_compact_ptr_storage_base,
      alloc_aware<Alloc>,
      inplace_ptr<T> {
  template <class... Args>
  explicit shared_compact_ptr_storage(const Alloc& alloc, Args&&... args)
      : alloc_aware<Alloc>(alloc),
        inplace_ptr<T>(std::in_place, std::forward<Args>(args)...) {}
};
template <class T, class Alloc>
class shared_compact_ptr PROD_TR_IF_ELIGIBLE
    : public indirect_ptr<shared_compact_ptr_storage<T, Alloc>> {
  using Storage = shared_compact_ptr_storage<T, Alloc>;

public:
  template <class... Args>
  shared_compact_ptr(const Alloc& alloc, Args&&... args)
      : indirect_ptr<Storage>(
            allocate<Storage>(alloc, alloc, std::forward<Args>(args)...)) {}
  shared_compact_ptr(const shared_compact_ptr& rhs) noexcept
      : indirect_ptr<Storage>(rhs.ptr_) {
    this->ptr_->ref_count.fetch_add(1, std::memory_order::relaxed);
  }
  shared_compact_ptr(shared_compact_ptr&& rhs) = delete;
  ~shared_compact_ptr() noexcept(std::is_nothrow_destructible_v<T>) {
    if (this->ptr_->ref_count.fetch_sub(1, std::memory_order::acq_rel) == 1) {
      deallocate(this->ptr_->alloc, this->ptr_);
    }
  }
};

struct strong_weak_compact_ptr_storage_base {
  std::atomic_long strong_count = 1, weak_count = 1;
};
template <class T, class Alloc>
struct strong_weak_compact_ptr_storage : strong_weak_compact_ptr_storage_base,
                                         alloc_aware<Alloc> {
  template <class... Args>
  explicit strong_weak_compact_ptr_storage(const Alloc& alloc, Args&&... args)
      : alloc_aware<Alloc>(alloc) {
    std::construct_at(reinterpret_cast<T*>(&value),
                      std::forward<Args>(args)...);
  }

  alignas(alignof(T)) std::byte value[sizeof(T)];
};
template <class T, class Alloc>
class weak_compact_ptr;
template <class T, class Alloc>
class strong_compact_ptr PROD_TR_IF_ELIGIBLE
    : public indirect_ptr<strong_weak_compact_ptr_storage<T, Alloc>> {
  using Storage = strong_weak_compact_ptr_storage<T, Alloc>;

public:
  explicit strong_compact_ptr(Storage* ptr) noexcept
      : indirect_ptr<Storage>(ptr) {}
  template <class... Args>
  strong_compact_ptr(const Alloc& alloc, Args&&... args)
      : indirect_ptr<Storage>(
            allocate<Storage>(alloc, alloc, std::forward<Args>(args)...)) {}
  strong_compact_ptr(const strong_compact_ptr& rhs) noexcept
      : indirect_ptr<Storage>(rhs.ptr_) {
    this->ptr_->strong_count.fetch_add(1, std::memory_order::relaxed);
  }
  strong_compact_ptr(strong_compact_ptr&& rhs) = delete;
  ~strong_compact_ptr() noexcept(std::is_nothrow_destructible_v<T>) {
    if (this->ptr_->strong_count.fetch_sub(1, std::memory_order::acq_rel) ==
        1) {
      std::destroy_at(operator->());
      if (this->ptr_->weak_count.fetch_sub(1u, std::memory_order::release) ==
          1) {
        deallocate(this->ptr_->alloc, this->ptr_);
      }
    }
  }
  auto get_weak() const noexcept {
    return converter{[ptr = this->ptr_]<class F>(
                         std::in_place_type_t<proxy<F>>) noexcept -> proxy<F> {
      ptr->weak_count.fetch_add(1, std::memory_order::relaxed);
      return proxy<F>{std::in_place_type<weak_compact_ptr<T, Alloc>>, ptr};
    }};
  }
  T* operator->() noexcept {
    return std::launder(reinterpret_cast<T*>(&this->ptr_->value));
  }
  const T* operator->() const noexcept {
    return std::launder(reinterpret_cast<const T*>(&this->ptr_->value));
  }
  T& operator*() & noexcept { return *operator->(); }
  const T& operator*() const& noexcept { return *operator->(); }
  T&& operator*() && noexcept { return std::move(*operator->()); }
  const T&& operator*() const&& noexcept { return std::move(*operator->()); }
};
template <class T, class Alloc>
class weak_compact_ptr PROD_TR_IF_ELIGIBLE {
public:
  explicit weak_compact_ptr(
      strong_weak_compact_ptr_storage<T, Alloc>* ptr) noexcept
      : ptr_(ptr) {}
  weak_compact_ptr(const strong_compact_ptr<T, Alloc>& rhs) noexcept
      : ptr_(rhs.ptr_) {
    ptr_->weak_count.fetch_add(1, std::memory_order::relaxed);
  }
  weak_compact_ptr(const weak_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) {
    ptr_->weak_count.fetch_add(1, std::memory_order::relaxed);
  }
  weak_compact_ptr(weak_compact_ptr&& rhs) = delete;
  ~weak_compact_ptr() noexcept {
    if (ptr_->weak_count.fetch_sub(1u, std::memory_order::acq_rel) == 1) {
      deallocate(ptr_->alloc, ptr_);
    }
  }
  auto lock() const noexcept {
    return converter{[ptr = this->ptr_]<class F>(
                         std::in_place_type_t<proxy<F>>) noexcept -> proxy<F> {
      long ref_count = ptr->strong_count.load(std::memory_order::relaxed);
      do {
        if (ref_count == 0) {
          return proxy<F>{};
        }
      } while (!ptr->strong_count.compare_exchange_weak(
          ref_count, ref_count + 1, std::memory_order::relaxed));
      return proxy<F>{std::in_place_type<strong_compact_ptr<T, Alloc>>, ptr};
    }};
  }

private:
  strong_weak_compact_ptr_storage<T, Alloc>* ptr_;
};

看起来像是手写了几个智能指针,暂时没看明白为什么
gpt给的回答里我挑几个看起来靠谱的:

  • 自定义控制块,把控制块和指针放在一起
  • 标准库的智能指针,operator*的转发不够精细(指qualifier)
  • 更轻,减少可能的堆分配

然后可以注意到 weak 和 strong 的实现稍微有点区别,weak 不能直接访问所以保留裸指针;strong 需要直接访问所以直接继承 indirect_ptr

template <class F, class T, class Alloc, class... Args>
constexpr proxy<F> allocate_proxy_impl(const Alloc& alloc, Args&&... args) {
  if constexpr (proxiable<allocated_ptr<T, Alloc>, F>) {
    return proxy<F>{std::in_place_type<allocated_ptr<T, Alloc>>, alloc,
                    std::forward<Args>(args)...};
  } else {
    return proxy<F>{std::in_place_type<compact_ptr<T, Alloc>>, alloc,
                    std::forward<Args>(args)...};
  }
}
template <class F, class T, class... Args>
constexpr proxy<F> make_proxy_impl(Args&&... args) {
  if constexpr (proxiable<inplace_ptr<T>, F>) {
    return proxy<F>{std::in_place_type<inplace_ptr<T>>, std::in_place,
                    std::forward<Args>(args)...};
  } else {
    return allocate_proxy_impl<F, T>(std::allocator<void>{},
                                     std::forward<Args>(args)...);
  }
}
template <class F, class T, class Alloc, class... Args>
constexpr proxy<F> allocate_proxy_shared_impl(const Alloc& alloc,
                                              Args&&... args) {
  if constexpr (weak_ownership_support_traits<F>::applicable) {
    return proxy<F>{std::in_place_type<strong_compact_ptr<T, Alloc>>, alloc,
                    std::forward<Args>(args)...};
  } else {
    return proxy<F>{std::in_place_type<shared_compact_ptr<T, Alloc>>, alloc,
                    std::forward<Args>(args)...};
  }
}
template <class F, class T, class... Args>
constexpr proxy<F> make_proxy_shared_impl(Args&&... args) {
  return allocate_proxy_shared_impl<F, T>(std::allocator<void>{},
                                          std::forward<Args>(args)...);
}

首先 make_proxy 如果能 in-place 就直接走
否则到 allocate_proxy 如果能 proxy 就用 allocated_ptr 否则用 compact_ptr
然后如果需要共享语义,就必定不能放在对象内部,必定能 proxy,能 weak 就走 strong_compact_ptr,否则就 shared_compact_ptr

template <facade F>
struct observer_facade;
template <facade F>
using proxy_view = proxy<observer_facade<F>>;

template <facade F>
struct weak_facade;
template <facade F>
using weak_proxy = proxy<weak_facade<F>>;

template <class T, class F>
concept inplace_proxiable_target = proxiable<details::inplace_ptr<T>, F>;

template <class T, class F>
concept proxiable_target =
    proxiable<details::observer_ptr<T&, const T&, T&&, const T&&>,
              observer_facade<F>>;

template <class T>
  requires(is_bitwise_trivially_relocatable_v<T>)
struct is_bitwise_trivially_relocatable<details::inplace_ptr<T>>
    : std::true_type {};

template <facade F, class T, class... Args>
constexpr proxy<F> make_proxy_inplace(Args&&... args) noexcept(
    std::is_nothrow_constructible_v<T, Args...>)
  requires(std::is_constructible_v<T, Args...>)
{
  return proxy<F>{std::in_place_type<details::inplace_ptr<T>>, std::in_place,
                  std::forward<Args>(args)...};
}
template <facade F, class T, class U, class... Args>
constexpr proxy<F>
    make_proxy_inplace(std::initializer_list<U> il, Args&&... args) noexcept(
        std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>)
  requires(std::is_constructible_v<T, std::initializer_list<U>&, Args...>)
{
  return proxy<F>{std::in_place_type<details::inplace_ptr<T>>, std::in_place,
                  il, std::forward<Args>(args)...};
}
template <facade F, class T>
constexpr proxy<F> make_proxy_inplace(T&& value) noexcept(
    std::is_nothrow_constructible_v<std::decay_t<T>, T>)
  requires(std::is_constructible_v<std::decay_t<T>, T>)
{
  return proxy<F>{std::in_place_type<details::inplace_ptr<std::decay_t<T>>>,
                  std::in_place, std::forward<T>(value)};
}

template <facade F, class T>
constexpr proxy_view<F> make_proxy_view(T& value) noexcept {
  return proxy_view<F>{
      details::observer_ptr<T&, const T&, T&&, const T&&>{value}};
}

核心就是构造一个 proxy_view,view of proxy,观察者
然后其下有各种各样的从 facade 和 Args 构造 proxy 的构造函数,略去

分发

从这里开始,会有许多丑陋的宏代码出现。
我会给一个展开版本,但是不展开限定符

template <bool Expl, bool Nullable>
struct cast_dispatch_base {
    template <class P, class D, class T>
    struct accessor<P, D, T()> {
        explicit(Expl) operator T() {
            if constexpr (Nullable) {
                if (!static_cast<const P&>(*this).has_value()) {
                    return nullptr;
                }
            }
            return proxy_invoke<D, T()>(static_cast<P&>(*this));
        }
    };
};

非常简单的分发,注意写在模板参数里的 T() 表示无参数,返回类型为 T 的函数类型

struct upward_conversion_dispatch : cast_dispatch_base<false, true>, internal_dispatch {
    template <class P, class F>
    auto operator()(std::in_place_type_t<P>, proxy<F>&& self) const {
        return converter{
            [&self]<class F2>(std::in_place_type_t<proxy<F2>>) {
                if constexpr (is_bitwise_trivially_relocatable_v<P>) {
                    proxy<F2> ret;
                    proxy_helper::trivially_relocate<P>(self, ret);
                    return ret;
                } else {
                    proxy_helper::resetting_guard<P, F> guard{self};
                    return proxy<F2>{proxy_helper::get_ptr<P, F, qualifier_type::rv>(std::move(self))};
                }
            }
        };
    }

    template <class P, class F>
    decltype(auto) operator()(std::in_place_type_t<P>, const proxy<F>& self) const {
        return proxy_helper::get_ptr<P, F, qualifier_type::const_lv>(self);
    }
};

拷贝构造语义

template <class O, class I>
struct add_tuple_reduction : std::type_identity<O> {};
template <class... Os, class I>
  requires(!std::is_same_v<I, Os> && ...)
struct add_tuple_reduction<std::tuple<Os...>, I>
    : std::type_identity<std::tuple<Os..., I>> {};
template <class T, class U>
using add_tuple_t = typename add_tuple_reduction<T, U>::type;
template <class O, class... Is>
using merge_tuple_impl_t = recursive_reduction_t<add_tuple_t, O, Is...>;
template <class T, class U>
using merge_tuple_t = instantiated_t<merge_tuple_impl_t, U, T>;

加入 tuple,合并 tuple,requires 用来去重,下同

template <bool IsDirect, class D>
struct merge_conv_traits {
  template <class... Os>
  using type = conv_impl<IsDirect, D, Os...>;
};
template <class C1, class C2>
using merge_conv_t = instantiated_t<
    merge_conv_traits<C1::is_direct, typename C1::dispatch_type>::template type,
    merge_tuple_t<typename C1::overload_types, typename C2::overload_types>>;

template <class Cs1, class C2, class C>
struct add_conv_reduction;
template <class... Cs1, class C2, class... Cs3, class C>
struct add_conv_reduction<std::tuple<Cs1...>, std::tuple<C2, Cs3...>, C>
    : add_conv_reduction<std::tuple<Cs1..., C2>, std::tuple<Cs3...>, C> {};
template <class... Cs1, class C2, class... Cs3, class C>
  requires(
      C::is_direct == C2::is_direct &&
      std::is_same_v<typename C::dispatch_type, typename C2::dispatch_type>)
struct add_conv_reduction<std::tuple<Cs1...>, std::tuple<C2, Cs3...>, C>
    : std::type_identity<std::tuple<Cs1..., merge_conv_t<C2, C>, Cs3...>> {};
template <class... Cs, class C>
struct add_conv_reduction<std::tuple<Cs...>, std::tuple<>, C>
    : std::type_identity<std::tuple<
          Cs..., merge_conv_t<
                     conv_impl<C::is_direct, typename C::dispatch_type>, C>>> {
};
template <class Cs, class C>
using add_conv_t = typename add_conv_reduction<std::tuple<>, Cs, C>::type;

合并 convention

template <class O>
using observer_upward_conversion_overload =
    proxy_view<typename overload_traits<O>::return_type::facade_type>()
        const noexcept;
template <class O, class I>
struct observer_upward_conversion_conv_reduction : std::type_identity<O> {};
template <class... Os, class O>
  requires(!std::is_same_v<Os, observer_upward_conversion_overload<O>> && ...)
struct observer_upward_conversion_conv_reduction<
    conv_impl<true, upward_conversion_dispatch, Os...>, O>
    : std::type_identity<conv_impl<true, upward_conversion_dispatch, Os...,
                                   observer_upward_conversion_overload<O>>> {};
template <class... Os>
using observer_upward_conversion_conv = recursive_reduction_t<
    reduction_traits<observer_upward_conversion_conv_reduction>::template type,
    conv_impl<true, upward_conversion_dispatch>, Os...>;

template <class D, class... Os>
using observer_indirect_conv = conv_impl<false, D, Os...>;

从 facade 升到 proxy_view


template <class C>
struct observer_conv_traits : std::type_identity<void> {};
template <class C>
  requires(
      C::is_direct &&
      std::is_same_v<typename C::dispatch_type, upward_conversion_dispatch>)
struct observer_conv_traits<C>
    : std::type_identity<instantiated_t<observer_upward_conversion_conv,
                                        typename C::overload_types>> {};
template <class C>
  requires(!C::is_direct)
struct observer_conv_traits<C>
    : std::type_identity<
          instantiated_t<observer_indirect_conv, typename C::overload_types,
                         typename C::dispatch_type>> {};
template <class... Cs>
struct observer_facade_conv_impl {
  using convention_types =
      composite_t<std::tuple<>, typename observer_conv_traits<Cs>::type...>;
};

template <class... Rs>
struct observer_facade_refl_impl {
  using reflection_types =
      composite_t<std::tuple<>, std::conditional_t<Rs::is_direct, void, Rs>...>;
};

和上面差不多,如果不是 is_direct 的话用void占位,或者偏特化

template <class P>
auto weak_lock_impl(const P& self) noexcept
  requires(requires { static_cast<bool>(self.lock()); })
{
  return converter{[&self]<class F>(std::in_place_type_t<proxy<F>>) noexcept {
    auto strong = self.lock();
    return strong ? proxy<F>{std::move(strong)} : proxy<F>{};
  }};
}

内层 requires 要求可以 self 可以 lock,但是此时还没有 self,于是外层的 requires 提供了一个模拟环境,神秘语法
这个 lock 的要求约等于 weak_ptr 语义,把 weak_ptr convert 到 proxy<strong_ptr>

template <class O, class I>
struct weak_conv_reduction : std::type_identity<O> {};
template <class... Cs, class I>
  requires(
      I::is_direct &&
      std::is_same_v<typename I::dispatch_type, upward_conversion_dispatch>)
struct weak_conv_reduction<std::tuple<Cs...>, I>
    : std::type_identity<
          std::tuple<Cs..., instantiated_t<weak_upward_conversion_conv,
                                           typename I::overload_types>>> {};
template <class F, class... Cs>
struct weak_facade_impl {
  using convention_types = recursive_reduction_t<
      reduction_traits<weak_conv_reduction>::template type,
      std::tuple<conv_impl<true, weak_mem_lock, proxy<F>() const noexcept>>,
      Cs...>;
  using reflection_types = std::tuple<>;
};

和上面差不多,合并,递归

template <class Cs, class Rs, std::size_t MaxSize, std::size_t MaxAlign,
          constraint_level Copyability, constraint_level Relocatability,
          constraint_level Destructibility>
struct basic_facade_builder {
  template <class D, details::extended_overload... Os>
    requires(sizeof...(Os) > 0u)
  using add_indirect_convention = basic_facade_builder<
      details::add_conv_t<Cs, details::conv_impl<false, D, Os...>>, Rs, MaxSize,
      MaxAlign, Copyability, Relocatability, Destructibility>;
  template <class D, details::extended_overload... Os>
    requires(sizeof...(Os) > 0u)
  using add_direct_convention = basic_facade_builder<
      details::add_conv_t<Cs, details::conv_impl<true, D, Os...>>, Rs, MaxSize,
      MaxAlign, Copyability, Relocatability, Destructibility>;
  template <class D, details::extended_overload... Os>
    requires(sizeof...(Os) > 0u)
  using add_convention = add_indirect_convention<D, Os...>;
  template <class R>
  using add_indirect_reflection = basic_facade_builder<
      Cs, details::add_tuple_t<Rs, details::refl_impl<false, R>>, MaxSize,
      MaxAlign, Copyability, Relocatability, Destructibility>;
  template <class R>
  using add_direct_reflection = basic_facade_builder<
      Cs, details::add_tuple_t<Rs, details::refl_impl<true, R>>, MaxSize,
      MaxAlign, Copyability, Relocatability, Destructibility>;
  template <class R>
  using add_reflection = add_indirect_reflection<R>;
  template <facade F, bool WithUpwardConversion = false>
  using add_facade = basic_facade_builder<
      details::merge_facade_conv_t<Cs, F, WithUpwardConversion>,
      details::merge_tuple_t<Rs, typename F::reflection_types>,
      details::merge_size(MaxSize, F::max_size),
      details::merge_size(MaxAlign, F::max_align),
      details::merge_constraint(Copyability, F::copyability),
      details::merge_constraint(Relocatability, F::relocatability),
      details::merge_constraint(Destructibility, F::destructibility)>;
  template <std::size_t PtrSize,
            std::size_t PtrAlign = details::max_align_of(PtrSize)>
    requires(std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u)
  using restrict_layout =
      basic_facade_builder<Cs, Rs, details::merge_size(MaxSize, PtrSize),
                           details::merge_size(MaxAlign, PtrAlign), Copyability,
                           Relocatability, Destructibility>;
  template <constraint_level CL>
  using support_copy =
      basic_facade_builder<Cs, Rs, MaxSize, MaxAlign,
                           details::merge_constraint(Copyability, CL),
                           Relocatability, Destructibility>;
  template <constraint_level CL>
  using support_relocation =
      basic_facade_builder<Cs, Rs, MaxSize, MaxAlign, Copyability,
                           details::merge_constraint(Relocatability, CL),
                           Destructibility>;
  template <constraint_level CL>
  using support_destruction =
      basic_facade_builder<Cs, Rs, MaxSize, MaxAlign, Copyability,
                           Relocatability,
                           details::merge_constraint(Destructibility, CL)>;
  template <template <class> class Skill>
  using add_skill = Skill<basic_facade_builder>;
  using build = details::facade_impl<
      Cs, Rs,
      MaxSize == details::invalid_size ? sizeof(details::ptr_prototype)
                                       : MaxSize,
      MaxAlign == details::invalid_size ? alignof(details::ptr_prototype)
                                        : MaxAlign,
      Copyability == details::invalid_cl ? constraint_level::none : Copyability,
      Relocatability == details::invalid_cl ? constraint_level::trivial
                                            : Relocatability,
      Destructibility == details::invalid_cl ? constraint_level::nothrow
                                             : Destructibility>;
  basic_facade_builder() = delete;
};

using facade_builder =
    basic_facade_builder<std::tuple<>, std::tuple<>, details::invalid_size,
                         details::invalid_size, details::invalid_cl,
                         details::invalid_cl, details::invalid_cl>;

封装默认的 facade builder,大部分操作都在这里

struct view_conversion_dispatch : cast_dispatch_base<false, true> {
  template <class T>
  PRO4D_STATIC_CALL(auto, T& value) noexcept
    requires(requires {
      { std::addressof(*value) } noexcept;
    })
  {
    return observer_ptr<decltype(*value), decltype(*std::as_const(value)),
                        decltype(*std::move(value)),
                        decltype(*std::move(std::as_const(value)))>{*value};
  }
};
template <class F>
using view_conversion_overload = proxy_view<F>() noexcept;

struct weak_conversion_dispatch : cast_dispatch_base<false, true> {
  template <class P>
  PRO4D_STATIC_CALL(auto, const P& self) noexcept
    requires(requires(const typename P::weak_type& w) {
      { w.lock() } noexcept -> std::same_as<P>;
    } && std::is_convertible_v<const P&, typename P::weak_type>)
  {
    return typename P::weak_type{self};
  }
  template <class T, class Alloc>
  PRO4D_STATIC_CALL(auto, const strong_compact_ptr<T, Alloc>& self) noexcept {
    return self.get_weak();
  }
};
template <class F>
using weak_conversion_overload = weak_proxy<F>() const noexcept;

直接从名字就能读出来干什么的
注意到有个 add_facade,事实上可以直接在 builder 里添加 facade

rtti

struct proxy_cast_context {
  const std::type_info* type_ptr;
  bool is_ref;
  bool is_const;
  void* result_ptr;
};

struct proxy_cast_dispatch;
template <class Self, class D, class O>
struct proxy_cast_accessor_impl {
  using QualifiedSelf = add_qualifier_t<Self, overload_traits<O>::qualifier>;
  template <class T>
  friend T proxy_cast(QualifiedSelf self) {
    static_assert(!std::is_rvalue_reference_v<T>);
    if constexpr (std::is_lvalue_reference_v<T>) {
      using U = std::remove_reference_t<T>;
      void* result = nullptr;
      proxy_cast_context ctx{.type_ptr = &typeid(T),
                             .is_ref = true,
                             .is_const = std::is_const_v<U>,
                             .result_ptr = &result};
      proxy_invoke<D, O>(static_cast<QualifiedSelf>(self), ctx);
      if (result == nullptr) [[unlikely]] {
        PRO4D_THROW(bad_proxy_cast{});
      }
      return *static_cast<U*>(result);
    } else {
      std::optional<std::remove_const_t<T>> result;
      proxy_cast_context ctx{.type_ptr = &typeid(T),
                             .is_ref = false,
                             .is_const = false,
                             .result_ptr = &result};
      proxy_invoke<D, O>(static_cast<QualifiedSelf>(self), ctx);
      if (!result.has_value()) [[unlikely]] {
        PRO4D_THROW(bad_proxy_cast{});
      }
      return std::move(*result);
    }
  }
  template <class T>
  friend T* proxy_cast(std::remove_reference_t<QualifiedSelf>* self) noexcept
    requires(std::is_lvalue_reference_v<QualifiedSelf>)
  {
    void* result = nullptr;
    proxy_cast_context ctx{.type_ptr = &typeid(T),
                           .is_ref = true,
                           .is_const = std::is_const_v<T>,
                           .result_ptr = &result};
    proxy_invoke<D, O>(*self, ctx);
    return static_cast<T*>(result);
  }
};

手写版 rtti
这个 proxy_invoke 按道理是要更新 ctx.result_ptr,但是我没找到对应的重载

template <class P, class D>
struct accessor<P, D, void(proxy_cast_context)> : proxy_cast_accessor_impl<P, D, void(proxy_cast_context)> {};

struct proxy_cast_dispatch {
    template <class T>
    void operator()(T&& self, proxy_cast_context ctx) const {
        if (typeid(T) == *ctx.type_ptr) [[likely]] {
            if (ctx.is_ref) {
                if constexpr (std::is_lvalue_reference_v<T>) {
                    if (ctx.is_const || !std::is_const_v<T>) [[likely]] {
                        *static_cast<void**>(ctx.result_ptr) = (void*)std::addressof(self);
                    }
                }
            } else {
                if constexpr (std::is_constructible_v<std::decay_t<T>, T>) {
                    static_cast<std::optional<std::decay_t<T>>*>(ctx.result_ptr)
                        ->emplace(std::forward<T>(self));
                }
            }
        }
    }

    template <class P, class D, class... ProOs>
    struct accessor : accessor<P, D, void(proxy_cast_context)>... {};
};

根据是否为引用类型,填写 ctx.result_ptr

struct proxy_typeid_reflector {
  template <class T>
  constexpr explicit proxy_typeid_reflector(std::in_place_type_t<T>)
      : info(&typeid(T)) {}
  constexpr proxy_typeid_reflector(const proxy_typeid_reflector&) = default;

  template <class Self, class R>
  struct accessor {
    friend const std::type_info& proxy_typeid(const Self& self) noexcept {
      const proxy_typeid_reflector& refl = proxy_reflect<R>(self);
      return *refl.info;
    }
  };

  const std::type_info* info;
};

根据 typeid 拿到 typeinfo,简单的反射

operand

template <std::size_t N>
struct sign {
  consteval sign(const char (&str)[N + 1]) {
    if (str[N] != '\0') {
      std::abort();
    }
    for (std::size_t i = 0; i < N; ++i) {
      value[i] = str[i];
    }
  }

  char value[N];
};
template <std::size_t N>
sign(const char (&str)[N]) -> sign<N - 1u>;

编译期将字符串字面量保存,基本上是用来 parse 出 operator 的
然后是一大坨 operator dispatch,总之就是实现这些 operator,用 proxy_invoke 转发到对应实现,并构造对应的 accessor

struct implicit_conversion_dispatch
    : details::cast_dispatch_base<false, false> {
  template <class T>
  PRO4D_STATIC_CALL(T&&, T&& self) noexcept {
    return std::forward<T>(self);
  }
};
struct explicit_conversion_dispatch : details::cast_dispatch_base<true, false> {
  template <class T>
  PRO4D_STATIC_CALL(auto, T&& self) noexcept {
    return details::converter{
        [&self]<class U>(std::in_place_type_t<U>) noexcept(
            std::is_nothrow_constructible_v<U, T>)
          requires(std::is_constructible_v<U, T>)
        { return U{std::forward<T>(self)}; }};
  }
};
using conversion_dispatch = explicit_conversion_dispatch;

template <class D>
struct weak_dispatch : D {
  using D::operator();
  template <class... Args>
  [[noreturn]] PRO4D_STATIC_CALL(details::wildcard, Args&&...)
    requires(!std::is_invocable_v<D, Args...>)
  {
    PRO4D_THROW(not_implemented{});
  }
};

隐式转换,显示转换,和对外接口

源代码至此结束,接下来会有一篇应用篇

posted @ 2025-09-21 00:01  123789456ye  阅读(12)  评论(0)    收藏  举报