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{});
}
};
隐式转换,显示转换,和对外接口
源代码至此结束,接下来会有一篇应用篇
There is a negligible beginning in all great action and thought.

浙公网安备 33010602011771号