123789456ye

已AFO

Proxy 库解析(二)

前言

本篇的主要内容是拼接各种各样的 meta 到 facade 和 proxy

refl meta

template <bool IsDirect, class R>
struct refl_meta {
  template <class P>
    requires(IsDirect)
  constexpr explicit refl_meta(std::in_place_type_t<P>)
      : reflector(std::in_place_type<P>) {}
  template <class P>
    requires(!IsDirect)
  constexpr explicit refl_meta(std::in_place_type_t<P>)
      : reflector(
            std::in_place_type<typename std::pointer_traits<P>::element_type>) {
  }

  R reflector;
};

这里写两个 requires 应该主要考虑的是报错可读性,不然写个 if constexpr 是一样的

template <class R>
struct basic_refl_traits : inapplicable_traits {};
template <class R>
  requires(requires { typename R::reflector_type; } &&
           is_is_direct_well_formed<R>())
struct basic_refl_traits<R> : applicable_traits {};

template <class T, bool IsDirect, class R>
consteval bool is_reflector_well_formed() {
  if constexpr (IsDirect) {
    if constexpr (std::is_constructible_v<R, std::in_place_type_t<T>>) {
      if constexpr (is_consteval([] { return R(std::in_place_type<T>); })) {
        return true;
      }
    }
  } else {
    return is_reflector_well_formed<
        typename std::pointer_traits<T>::element_type, true, R>();
  }
  return false;
}

检查是否可以可以反射元信息,应该见过很多次了
感觉支持 C++26 的话可以用标准库的反射换掉这些

template <class R>
struct refl_traits {
  using meta = refl_meta<R::is_direct, typename R::reflector_type>;

  template <class P>
  static constexpr bool applicable_ptr =
      is_reflector_well_formed<P, R::is_direct, typename R::reflector_type>();
};

refl_meta 主要保存 is_direct 和 reflector_type(也就是 reflector 的类型)

struct copy_dispatch {
  template <class T, class F>
  PRO4D_STATIC_CALL(void, const T& self, proxy<F>& rhs) noexcept(
      std::is_nothrow_copy_constructible_v<T>) {
    std::construct_at(std::addressof(rhs), self);
  }
};
struct relocate_dispatch : internal_dispatch {
  template <class P, class F>
  PRO4D_STATIC_CALL(
      void, std::in_place_type_t<P>, proxy<F>&& self,
      proxy<F>&
          rhs) noexcept(relocatability_traits<P, constraint_level::nothrow>::
                            applicable) {
    if constexpr (is_bitwise_trivially_relocatable_v<P>) {
      proxy_helper::trivially_relocate<P>(self, rhs);
    } else {
      proxy_helper::resetting_guard<P, F> guard{self};
      std::construct_at(
          std::addressof(rhs),
          proxy_helper::get_ptr<P, F, qualifier_type::rv>(std::move(self)));
    }
  }
};
struct destroy_dispatch {
  template <class T>
  PRO4D_STATIC_CALL(void, T& self) noexcept(std::is_nothrow_destructible_v<T>) {
    std::destroy_at(&self);
  }
};

逻辑很简单,主要对比一下能直接移动(relocate),和不能直接移动的区别是什么

template <class C, class F, bool IsDirect>
struct conv_accessor_traits : std::type_identity<void> {};
template <class C, class F>
  requires(!C::is_direct)
struct conv_accessor_traits<C, F, false>
    : std::type_identity<typename conv_traits<C, F>::template accessor<
          proxy_indirect_accessor<F>>> {};
template <class C, class F>
  requires(C::is_direct)
struct conv_accessor_traits<C, F, true>
    : std::type_identity<
          typename conv_traits<C, F>::template accessor<proxy<F>>> {};
template <class C, class F, bool IsDirect>
using conv_accessor_t = typename conv_accessor_traits<C, F, IsDirect>::type;

根据 is_direct 分发 convention(conv)

template <class R, class F, bool IsDirect>
struct refl_accessor_traits : std::type_identity<void> {};
template <class R, class F>
  requires(!R::is_direct)
struct refl_accessor_traits<R, F, false>
    : std::type_identity<
          accessor_t<typename R::reflector_type, proxy_indirect_accessor<F>,
                     typename R::reflector_type>> {};
template <class R, class F>
  requires(R::is_direct)
struct refl_accessor_traits<R, F, true>
    : std::type_identity<accessor_t<typename R::reflector_type, proxy<F>,
                                    typename R::reflector_type>> {};
template <class R, class F, bool IsDirect>
using refl_accessor_t = typename refl_accessor_traits<R, F, IsDirect>::type;

同上,分发 refl meta

template <class P>
struct ptr_traits : inapplicable_traits {};
template <class P>
  requires(requires { typename std::pointer_traits<P>::element_type; })
struct ptr_traits<P> : applicable_traits {};
template <class F>
struct ptr_traits<proxy<F>> : inapplicable_traits {};
template <>
struct ptr_traits<std::nullptr_t> : inapplicable_traits {};

判断是否为指针类型(实现了 element_type)

组装 facade

template <class... As>
struct composite_accessor : As... {};

template <class F, class... Cs>
struct facade_conv_traits_impl : inapplicable_traits {};
template <class F, class... Cs>
  requires(conv_traits<Cs, F>::applicable && ...)
struct facade_conv_traits_impl<F, Cs...> : applicable_traits {
  using conv_meta =
      composite_t<composite_meta<>, typename conv_traits<Cs, F>::meta...>;
  using conv_indirect_accessor =
      composite_t<composite_accessor<>, conv_accessor_t<Cs, F, false>...>;
  using conv_direct_accessor =
      composite_t<composite_accessor<>, conv_accessor_t<Cs, F, true>...>;

  template <class P>
  static constexpr bool conv_applicable_ptr =
      (conv_traits<Cs, F>::template applicable_ptr<P> && ...);
  template <bool IsDirect, class D, class O>
  static constexpr bool is_invocable =
      std::is_base_of_v<invocation_meta<F, IsDirect, D, O>, conv_meta>;
};

组合 conv_accessor ,顺便定义了 indirect 和 direct accessor
还记得 composite_t 怎么展开吗(

template <class F, class... Rs>
struct facade_refl_traits_impl {
  using refl_meta = composite_meta<typename refl_traits<Rs>::meta...>;
  using refl_indirect_accessor =
      composite_t<composite_accessor<>, refl_accessor_t<Rs, F, false>...>;
  using refl_direct_accessor =
      composite_t<composite_accessor<>, refl_accessor_t<Rs, F, true>...>;

  template <class P>
  static constexpr bool refl_applicable_ptr =
      (refl_traits<Rs>::template applicable_ptr<P> && ...);
};

同上,refl_accessor 版

template <class F>
struct facade_traits : inapplicable_traits {};
template <class F>
  requires(instantiated_t<facade_conv_traits_impl, typename F::convention_types,
                          F>::applicable)
struct facade_traits<F>
    : instantiated_t<facade_conv_traits_impl, typename F::convention_types, F>,
      instantiated_t<facade_refl_traits_impl, typename F::reflection_types, F> {
  using meta = composite_t<
      composite_meta<>,
      lifetime_meta_t<F, copy_dispatch, void(proxy<F>&) const noexcept,
                      void(proxy<F>&) const, F::copyability>,
      lifetime_meta_t<F, relocate_dispatch, void(proxy<F>&) && noexcept,
                      void(proxy<F>&) &&, F::relocatability>,
      lifetime_meta_t<F, destroy_dispatch, void() noexcept, void(),
                      F::destructibility>,
      typename facade_traits::conv_meta, typename facade_traits::refl_meta>;
  using indirect_accessor =
      composite_t<typename facade_traits::conv_indirect_accessor,
                  typename facade_traits::refl_indirect_accessor>;
  using direct_accessor = composite_t<
      typename facade_traits::conv_direct_accessor,
      typename facade_traits::refl_direct_accessor,
      std::conditional_t<F::relocatability == constraint_level::trivial, void,
                         tr_blocker>>;

  template <class P>
  static constexpr bool applicable_ptr =
      sizeof(P) <= F::max_size && alignof(P) <= F::max_align &&
      copyability_traits<P, F::copyability>::applicable &&
      relocatability_traits<P, F::relocatability>::applicable &&
      destructibility_traits<P, F::destructibility>::applicable &&
      facade_traits::template conv_applicable_ptr<P> &&
      facade_traits::template refl_applicable_ptr<P>;
};

最后,把所有 meta 全部拼起来,我们得到了完整的 facade
其中 tr_blocker 是用来 block trivially relocate 的

struct tr_blocker {
  tr_blocker() = default;
  tr_blocker(const tr_blocker&) noexcept {}
  tr_blocker& operator=(const tr_blocker&) noexcept { return *this; }
};

因为 tr_blocker 有非默认生成的构造函数,从而其为非 trivially move constructible 的,从而为非 trivially relocatable的
从而在 relocate_dispatch 的逻辑里,会强制到手动搬移的分支上

meta ptr

template <class M>
struct meta_ptr_indirect_impl {
  constexpr meta_ptr_indirect_impl() = default;
  template <class P>
  constexpr explicit meta_ptr_indirect_impl(std::in_place_type_t<P>) noexcept
      : ptr_(&storage<P>) {}
  bool has_value() const noexcept { return ptr_ != nullptr; }
  void reset() noexcept { ptr_ = nullptr; }
  const M* operator->() const noexcept { return ptr_; }

private:
  const M* ptr_;
  template <class P>
  static constexpr M storage{std::in_place_type<P>};
};
template <class M, class DM>
struct meta_ptr_direct_impl : private M {
  using M::M;
  bool has_value() const noexcept { return this->DM::dispatcher != nullptr; }
  void reset() noexcept { this->DM::dispatcher = nullptr; }
  const M* operator->() const noexcept { return this; }
};

indirect 和 direct 版本的 impl,相当直接
indirect 版会有一个静态的 storage,然后 ptr 指向这个,direct版就直接继承

template <class M>
struct meta_ptr_traits_impl : std::type_identity<meta_ptr_indirect_impl<M>> {};
template <class F, bool IsDirect, class D, class O, class... Ms>
struct meta_ptr_traits_impl<
    composite_meta<invocation_meta<F, IsDirect, D, O>, Ms...>>
    : std::type_identity<meta_ptr_direct_impl<
          composite_meta<invocation_meta<F, IsDirect, D, O>, Ms...>,
          invocation_meta<F, IsDirect, D, O>>> {};

分支之一,默认 indirect,如果能匹配上再偏特化到 direct

using ptr_prototype = void* [2];

struct meta_ptr_traits : std::type_identity<meta_ptr_indirect_impl<M>> {};
template <class M>
  requires(sizeof(M) <= sizeof(ptr_prototype) &&
           alignof(M) <= alignof(ptr_prototype) &&
           std::is_nothrow_default_constructible_v<M> &&
           std::is_trivially_copyable_v<M>)
struct meta_ptr_traits<M> : meta_ptr_traits_impl<M> {};

template <class M>
using meta_ptr = typename meta_ptr_traits<M>::type;

分支之二,先看满不满足小对象类型的条件,满足再到上一个分支,不满足直接 indirect
虽然这样子很丑,但是把两个判断合起来也很丑
ptr_prototype 的写法是为了跨平台

组装proxy

template <class T>
class inplace_ptr {
public:
  template <class... Args>
  explicit inplace_ptr(std::in_place_t, Args&&... args)
      : value_(std::forward<Args>(args)...) {}
  inplace_ptr() = default;
  inplace_ptr(const inplace_ptr&) = default;
  inplace_ptr(inplace_ptr&&) = default;
  inplace_ptr& operator=(const inplace_ptr&) = default;
  inplace_ptr& operator=(inplace_ptr&&) = default;

  T* operator->() noexcept { return std::addressof(value_); }
  const T* operator->() const noexcept { return std::addressof(value_); }
  T& operator*() & noexcept { return value_; }
  const T& operator*() const& noexcept { return value_; }
  T&& operator*() && noexcept { return std::move(value_); }
  const T&& operator*() const&& noexcept { return std::move(value_); }

private:
  [[PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE]]
  T value_;
};

保证对象在 inplace_str 段,而不是在堆上
至于用处,请看下集(

template <class F, bool IsDirect, class D, class O, class P, class... Args>
decltype(auto) invoke_impl(P&& p, Args&&... args) {
  auto dispatcher =
      proxy_helper::get_meta(p)
          .template invocation_meta<F, IsDirect, D, O>::dispatcher;
  return dispatcher(std::forward<P>(p), std::forward<Args>(args)...);
}

从 proxy 拿到 dispatcher 然后调用

template <class F, qualifier_type Q>
add_qualifier_t<proxy<F>, Q>
    as_proxy(add_qualifier_t<proxy_indirect_accessor<F>, Q> p) {
  return static_cast<add_qualifier_t<proxy<F>, Q>>(
      *reinterpret_cast<
          add_qualifier_ptr_t<inplace_ptr<proxy_indirect_accessor<F>>, Q>>(
          std::addressof(p)));
}

强转 indirect accessor 到 proxy,相当于解引用
inplace_ptr 保证不会出现地址问题,但是为什么前面的强转不需要?

template <class P, class F>
concept proxiable = facade<F> && details::facade_traits<F>::applicable &&
                    details::facade_traits<F>::template applicable_ptr<P>;

判断能不能用 proxy,最终的接口,首先 F 能用来构造 facade,其次 P 可以在这一 facade 下生成能用的 ptr

template <facade F>
class proxy_indirect_accessor
    : public details::facade_traits<F>::indirect_accessor {
  friend class details::inplace_ptr<proxy_indirect_accessor>;
  proxy_indirect_accessor() = default;
  proxy_indirect_accessor(const proxy_indirect_accessor&) = default;
  proxy_indirect_accessor& operator=(const proxy_indirect_accessor&) = default;
};

template <class D, class O, facade F, class... Args>
auto proxy_invoke(proxy_indirect_accessor<F>& p, Args&&... args) ->
    typename details::overload_traits<O>::return_type {
  return details::invoke_impl<F, false, D, O>(
      details::as_proxy<F, details::qualifier_type::lv>(p),
      std::forward<Args>(args)...);
}

indirect 的调用,先强转 proxy 再调用
后面一堆 qualifier 重载,不再赘述,按道理这些都能被 deducing this 解决,看他们什么时候升到23甚至26

template <class R, facade F>
const R& proxy_reflect(const proxy_indirect_accessor<F>& p) noexcept {
  return static_cast<const details::refl_meta<false, R>&>(
             details::proxy_helper::get_meta(
                 details::as_proxy<F, details::qualifier_type::const_lv>(p)))
      .reflector;
}
template <class R, facade F>
const R& proxy_reflect(const proxy<F>& p) noexcept {
  return static_cast<const details::refl_meta<true, R>&>(
             details::proxy_helper::get_meta(p))
      .reflector;
}

reflect 获得 meta,这里不太能强转,因为之前一大堆写的是两者分开的

template <facade F>
class proxy 
    : public details::facade_traits<F>::direct_accessor,
      public details::inplace_ptr<proxy_indirect_accessor<F>> {
  friend struct details::proxy_helper;
  static_assert(details::facade_traits<F>::applicable);

public:
  using facade_type = F;

  proxy() noexcept { initialize(); }
  proxy(std::nullptr_t) noexcept : proxy() {}
  proxy(const proxy&) noexcept
    requires(F::copyability == constraint_level::trivial)
  = default;
  proxy(const proxy& rhs) noexcept(F::copyability == constraint_level::nothrow)
    requires(F::copyability == constraint_level::nontrivial ||
             F::copyability == constraint_level::nothrow)
      : details::inplace_ptr<
            proxy_indirect_accessor<F>>() /* Make GCC happy */ {
    initialize(rhs);
  }
  
  proxy& operator=(std::nullptr_t) noexcept(F::destructibility >=
                                            constraint_level::nothrow)
    requires(F::destructibility >= constraint_level::nontrivial)
  {
    reset();
    return *this;
  }
  
  ~proxy()
    requires(F::destructibility == constraint_level::trivial)
  = default;
  ~proxy() noexcept(F::destructibility == constraint_level::nothrow)
    requires(F::destructibility == constraint_level::nontrivial ||
             F::destructibility == constraint_level::nothrow)
  {
    destroy();
  }

  bool has_value() const noexcept { return meta_.has_value(); }
  explicit operator bool() const noexcept { return meta_.has_value(); }
  void reset() noexcept(F::destructibility >= constraint_level::nothrow)
    requires(F::destructibility >= constraint_level::nontrivial)
  {
    destroy();
    initialize();
  }
  void swap(proxy& rhs) noexcept(F::relocatability >=
                                     constraint_level::nothrow ||
                                 F::copyability == constraint_level::trivial)
    requires(F::relocatability >= constraint_level::nontrivial ||
             F::copyability == constraint_level::trivial)
  {
    if constexpr (F::relocatability == constraint_level::trivial ||
                  F::copyability == constraint_level::trivial) {
      std::swap(meta_, rhs.meta_);
      std::swap(ptr_, rhs.ptr_);
    } else {
      if (meta_.has_value()) {
        if (rhs.meta_.has_value()) {
          proxy temp = std::move(*this);
          initialize(std::move(rhs));
          rhs.initialize(std::move(temp));
        } else {
          rhs.initialize(std::move(*this));
        }
      } else if (rhs.meta_.has_value()) {
        initialize(std::move(rhs));
      }
    }
  }
  template <class P, class... Args>
  constexpr P& emplace(Args&&... args) noexcept(
      std::is_nothrow_constructible_v<P, Args...> &&
      F::destructibility >= constraint_level::nothrow)
    requires(details::ptr_traits<P>::applicable &&
             std::is_constructible_v<P, Args...> &&
             F::destructibility >= constraint_level::nontrivial)
  {
    reset();
    return initialize<P>(std::forward<Args>(args)...);
  }
  template <class P, class U, class... Args>
  constexpr P& emplace(std::initializer_list<U> il, Args&&... args) noexcept(
      std::is_nothrow_constructible_v<P, std::initializer_list<U>&, Args...> &&
      F::destructibility >= constraint_level::nothrow)
    requires(details::ptr_traits<P>::applicable &&
             std::is_constructible_v<P, std::initializer_list<U>&, Args...> &&
             F::destructibility >= constraint_level::nontrivial)
  {
    reset();
    return initialize<P>(il, std::forward<Args>(args)...);
  }

  friend void swap(proxy& lhs, proxy& rhs) noexcept(noexcept(lhs.swap(rhs))) {
    lhs.swap(rhs);
  }
  friend bool operator==(const proxy& lhs, std::nullptr_t) noexcept {
    return !lhs.has_value();
  }

private:
  void initialize() {
    PRO4D_DEBUG(std::ignore = &pro_symbol_guard;)
    meta_.reset();
  }
  void initialize(const proxy& rhs)
    requires(F::copyability != constraint_level::none)
  {
    PRO4D_DEBUG(std::ignore = &pro_symbol_guard;)
    if (rhs.meta_.has_value()) {
      if constexpr (F::copyability == constraint_level::trivial) {
        std::ranges::uninitialized_copy(rhs.ptr_, ptr_);
        meta_ = rhs.meta_;
      } else {
        proxy_invoke<details::copy_dispatch,
                     void(proxy&) const noexcept(
                         F::copyability == constraint_level::nothrow)>(rhs,
                                                                       *this);
      }
    } else {
      meta_.reset();
    }
  }
  void initialize(proxy&& rhs)
    requires(F::relocatability != constraint_level::none)
  {
    PRO4D_DEBUG(std::ignore = &pro_symbol_guard;)
    if (rhs.meta_.has_value()) {
      if constexpr (F::relocatability == constraint_level::trivial) {
        std::ranges::uninitialized_copy(rhs.ptr_, ptr_);
        meta_ = rhs.meta_;
        rhs.meta_.reset();
      } else {
        proxy_invoke<details::relocate_dispatch,
                     void(proxy&) && noexcept(F::relocatability ==
                                              constraint_level::nothrow)>(
            std::move(rhs), *this);
      }
    } else {
      meta_.reset();
    }
  }
  template <class P, class... Args>
  constexpr P& initialize(Args&&... args) {
    PRO4D_DEBUG(std::ignore = &pro_symbol_guard;)
    P& result = *std::construct_at(reinterpret_cast<P*>(ptr_),
                                   std::forward<Args>(args)...);
    if constexpr (proxiable<P, F>) {
      meta_ = details::meta_ptr<typename details::facade_traits<F>::meta>{
          std::in_place_type<P>};
    } else {
      details::facade_traits<F>::template diagnose_proxiable<P>();
    }
    return result;
  }
  void destroy()
    requires(F::destructibility != constraint_level::none)
  {
    if constexpr (F::destructibility != constraint_level::trivial) {
      if (meta_.has_value()) {
        proxy_invoke<details::destroy_dispatch,
                     void() noexcept(F::destructibility ==
                                     constraint_level::nothrow)>(*this);
      }
    }
  }
  PRO4D_DEBUG(static inline void pro_symbol_guard(proxy& self,
                                                  const proxy& cself) {
    self.operator->();
    *self;
    *std::move(self);
    cself.operator->();
    *cself;
    *std::move(cself);
  })

  details::meta_ptr<typename details::facade_traits<F>::meta> meta_;
  alignas(F::max_align) std::byte ptr_[F::max_size];
};

classProxy 的定义,略去了若干构造和拷贝的重载
继承了 direct_accessor 和 indirect_accessor,friend 了 proxy_helper
核心就是,我们在 proxy 内保存了一个 meta,和一个 byte 格式的 ptr,按需在这个地址上构造到给定类型,或者强转

至此约1200行,大约50%

posted @ 2025-09-20 18:14  123789456ye  阅读(13)  评论(0)    收藏  举报