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%
There is a negligible beginning in all great action and thought.

浙公网安备 33010602011771号