Skip to content

Commit 07e6520

Browse files
authored
Reach metadata by conversion instead of by named lookup (#71)
meta_storage looked a meta up with get<M>(), which resolved to a static_cast over a flat set of base classes. That works only while every meta is a direct base of the storage, and cannot reach a meta held inside an aggregate. Replace it with proxy_meta, whose contained metas are reached through a conversion operator. A meta is contained if the metadata converts to it without throwing, so an aggregate that itself converts to a meta makes that meta reachable too. unique_types_t reduces the meta list so that each entry is contained by exactly one entry, namely itself, which is the property that keeps the conversion unambiguous: a duplicate collapses onto its leftmost occurrence, and a meta subsumed by an aggregate is replaced by that aggregate in place. Both storages now expose the metadata through operator*, so choosing between them is independent of how a meta is looked up. static_meta_storage holds a single meta and is chosen by size rather than by shape, which separates the metadata layout from the decision to hold it out of line. Under pointer authentication, assigning one storage from another signs a raw pointer, which meta_ptr now supports. No functional change.
1 parent ca59fe2 commit 07e6520

5 files changed

Lines changed: 263 additions & 110 deletions

File tree

include/proxy/v4/detail/core.h

Lines changed: 134 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ template <class F>
4141
struct basic_facade_traits;
4242

4343
template <class F>
44-
struct meta_storage;
44+
struct proxy_meta;
4545

4646
} // namespace detail
4747

@@ -246,15 +246,14 @@ struct proxy_helper {
246246
proxy<F>& p_;
247247
};
248248

249-
template <class F>
250-
static const meta_storage<F>& get_meta(const proxy<F>& p) noexcept {
251-
assert(p.has_value());
252-
return p.meta_;
249+
template <class M, class F>
250+
static const M& get_meta(const proxy<F>& p) noexcept {
251+
assert(p.meta_.has_value());
252+
return *p.meta_;
253253
}
254-
template <class F>
255-
static const meta_storage<F>&
256-
get_meta(const proxy_indirect_accessor<F>& p) noexcept {
257-
return get_meta(as_proxy<F, qualifier_type::const_lv>(p));
254+
template <class M, class F>
255+
static const M& get_meta(const proxy_indirect_accessor<F>& p) noexcept {
256+
return get_meta<M>(as_proxy<F, qualifier_type::const_lv>(p));
258257
}
259258
template <class F>
260259
static void* get_ptr(proxy<F>& p) noexcept {
@@ -659,6 +658,122 @@ struct specialization_traits<TT<Args...>, TT> : applicable_traits {};
659658
template <class T, template <class...> class TT>
660659
concept specialization_of = specialization_traits<T, TT>::applicable;
661660

661+
template <class O, class I, class T>
662+
struct first_containing_reduction : std::type_identity<O> {};
663+
template <class I, class T>
664+
struct first_containing_reduction<void, I, T>
665+
: std::conditional<std::is_nothrow_convertible_v<const I&, const T&>, I,
666+
void> {};
667+
668+
template <class O, class I>
669+
struct most_containing_reduction
670+
: std::conditional<std::is_nothrow_convertible_v<const I&, const O&>, I,
671+
O> {};
672+
template <class SFINAE, class O, class I>
673+
struct sfinae_unique_types_traits : std::type_identity<O> {};
674+
template <class... Ts, class U, class... Us>
675+
struct sfinae_unique_types_traits<
676+
std::enable_if_t<(std::is_nothrow_convertible_v<const Ts&, const U&> ||
677+
...)>,
678+
std::tuple<Ts...>, std::tuple<U, Us...>>
679+
: sfinae_unique_types_traits<void, std::tuple<Ts...>, std::tuple<Us...>> {};
680+
template <class... Ts, class U, class... Us>
681+
struct sfinae_unique_types_traits<
682+
std::enable_if_t<!(std::is_nothrow_convertible_v<const Ts&, const U&> ||
683+
...)>,
684+
std::tuple<Ts...>, std::tuple<U, Us...>>
685+
: sfinae_unique_types_traits<
686+
void,
687+
std::tuple<Ts...,
688+
recursive_reduction_t<
689+
reduction_t<most_containing_reduction>, U, Us...>>,
690+
std::tuple<Us...>> {};
691+
template <class T>
692+
using unique_types_t = sfinae_unique_types_traits<void, std::tuple<>, T>::type;
693+
694+
template <class T>
695+
concept nullable = requires(T v, const T cv) {
696+
{ v.reset() } noexcept;
697+
{ cv.has_value() } noexcept -> std::same_as<bool>;
698+
};
699+
700+
struct sentinel_meta {
701+
sentinel_meta() = default;
702+
template <class P>
703+
constexpr explicit sentinel_meta(std::in_place_type_t<P>) noexcept : v_(1) {}
704+
void reset() noexcept { v_ = 0; }
705+
bool has_value() const noexcept { return v_; }
706+
707+
private:
708+
std::ptrdiff_t v_;
709+
};
710+
711+
template <class... Ms>
712+
struct PRO4D_ENFORCE_EBO composite_meta : Ms... {
713+
composite_meta() = default;
714+
template <class P>
715+
constexpr explicit composite_meta(std::in_place_type_t<P>)
716+
: Ms(std::in_place_type<P>)... {}
717+
};
718+
719+
template <nullable First, class... Rest>
720+
struct proxy_meta_base_impl {
721+
constexpr proxy_meta_base_impl() noexcept {}
722+
template <class P>
723+
constexpr explicit proxy_meta_base_impl(std::in_place_type_t<P>)
724+
: value_(std::in_place_type<P>) {}
725+
proxy_meta_base_impl(const proxy_meta_base_impl& rhs) noexcept
726+
: proxy_meta_base_impl() {
727+
assign(rhs);
728+
}
729+
proxy_meta_base_impl& operator=(const proxy_meta_base_impl& rhs) noexcept {
730+
assign(rhs);
731+
return *this;
732+
}
733+
734+
template <class T>
735+
requires(std::is_nothrow_convertible_v<const First&, const T&> ||
736+
(std::is_nothrow_convertible_v<const Rest&, const T&> || ...))
737+
constexpr operator const T&() const noexcept {
738+
return static_cast<const recursive_reduction_t<
739+
reduction_t<first_containing_reduction, T>, void, First, Rest...>&>(
740+
value_);
741+
}
742+
743+
bool has_value() const noexcept {
744+
return static_cast<const First&>(value_).has_value();
745+
}
746+
void reset() noexcept { static_cast<First&>(value_).reset(); }
747+
748+
private:
749+
void assign(const proxy_meta_base_impl& rhs) noexcept {
750+
if (rhs.has_value()) {
751+
value_ = rhs.value_;
752+
} else {
753+
reset();
754+
}
755+
}
756+
757+
composite_meta<First, Rest...> value_;
758+
};
759+
template <nullable First>
760+
requires(std::is_trivially_copyable_v<First>)
761+
struct proxy_meta_base_impl<First> : First {
762+
using First::First;
763+
};
764+
765+
template <class... Ms>
766+
struct proxy_meta_base_traits
767+
: specialization_type_traits<proxy_meta_base_impl,
768+
unique_types_t<std::tuple<Ms...>>,
769+
sentinel_meta> {};
770+
template <nullable M, class... Ms>
771+
struct proxy_meta_base_traits<M, Ms...>
772+
: specialization_type_traits<proxy_meta_base_impl,
773+
unique_types_t<std::tuple<M, Ms...>>> {};
774+
template <class... Ms>
775+
using proxy_meta_base_t = typename proxy_meta_base_traits<Ms...>::type;
776+
662777
template <class P, class F, std::size_t ActualSize, std::size_t MaxSize>
663778
consteval void diagnose_proxiable_size_too_large() {
664779
static_assert(ActualSize <= MaxSize, "not proxiable due to size too large");
@@ -803,8 +918,8 @@ struct facade_traits : specialization_t<facade_conv_traits_impl,
803918
typename F::convention_types, F>,
804919
specialization_t<facade_refl_traits_impl,
805920
typename F::reflection_types, F> {
806-
using meta_storage_base = specialization_t<
807-
compact_facade_meta_traits::storage,
921+
using meta_base = specialization_t<
922+
proxy_meta_base_t,
808923
composite_t<std::tuple<>,
809924
lifetime_meta_t<copy_dispatch, void(void*) const noexcept,
810925
void(void*) const, F::copyability>,
@@ -849,8 +964,8 @@ struct facade_traits : specialization_t<facade_conv_traits_impl,
849964
};
850965

851966
template <class F>
852-
struct meta_storage : facade_traits<F>::meta_storage_base {
853-
using base = facade_traits<F>::meta_storage_base;
967+
struct proxy_meta : facade_traits<F>::meta_base {
968+
using base = facade_traits<F>::meta_base;
854969
using base::base;
855970
};
856971

@@ -889,7 +1004,7 @@ template <class F, bool IsDirect, class D, class O, class P, class... Args>
8891004
ret_t<O> invoke_impl(P&& p, Args&&... args) {
8901005
using Ctx = erased_context<IsDirect, D, O>;
8911006
Ctx ctx{proxy_helper::get_ptr(p)};
892-
const auto& inv = proxy_helper::get_meta(p).template get<invoker<Ctx, O>>();
1007+
const auto& inv = proxy_helper::get_meta<invoker<Ctx, O>>(p);
8931008
if constexpr (overload_traits<O>::this_qualifier == qualifier_type::rv) {
8941009
proxy_helper::meta_resetting_guard<F> guard{p};
8951010
return inv(ctx, std::forward<Args>(args)...);
@@ -935,8 +1050,7 @@ class proxy_indirect_accessor
9351050
}
9361051
template <class R>
9371052
friend const R& reflect(const proxy_indirect_accessor& p) noexcept {
938-
return detail::proxy_helper::get_meta(p)
939-
.template get<detail::reflection_meta<false, R>>()
1053+
return detail::proxy_helper::get_meta<detail::reflection_meta<false, R>>(p)
9401054
.reflector;
9411055
}
9421056
};
@@ -1153,8 +1267,7 @@ class proxy : public detail::facade_traits<F>::direct_accessor,
11531267
}
11541268
template <class R>
11551269
friend const R& reflect(const proxy& p) noexcept {
1156-
return detail::proxy_helper::get_meta(p)
1157-
.template get<detail::reflection_meta<true, R>>()
1270+
return detail::proxy_helper::get_meta<detail::reflection_meta<true, R>>(p)
11581271
.reflector;
11591272
}
11601273

@@ -1206,7 +1319,7 @@ class proxy : public detail::facade_traits<F>::direct_accessor,
12061319
P& result = *std::construct_at(reinterpret_cast<P*>(ptr_),
12071320
std::forward<Args>(args)...);
12081321
if constexpr (proxiable<P, F>) {
1209-
meta_ = detail::meta_storage<F>{std::in_place_type<P>};
1322+
meta_ = decltype(meta_){std::in_place_type<P>};
12101323
} else {
12111324
detail::facade_traits<F>::template diagnose_proxiable_noreturn<P>();
12121325
}
@@ -1234,7 +1347,8 @@ class proxy : public detail::facade_traits<F>::direct_accessor,
12341347
})
12351348

12361349
alignas(F::max_align) std::byte ptr_[F::max_size];
1237-
detail::meta_storage<F> meta_;
1350+
typename compact_facade_meta_traits::template storage<detail::proxy_meta<F>>
1351+
meta_;
12381352
};
12391353

12401354
template <class D, class O, facade F, class... Args>

include/proxy/v4/detail/facade_meta_traits.h

Lines changed: 42 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ class meta_ptr {
7878
schema());
7979
return *this;
8080
}
81+
meta_ptr& operator=(const T* p) noexcept {
82+
p_ = ptrauth_sign_unauthenticated(p, ptrauth_key_cxx_vtable_pointer,
83+
schema());
84+
return *this;
85+
}
8186
meta_ptr& operator=(std::nullptr_t) noexcept {
8287
p_ = nullptr;
8388
return *this;
@@ -102,12 +107,6 @@ template <class T, class Disc>
102107
using meta_ptr = const T*;
103108
#endif // PRO4D_HAS_PAC
104109

105-
template <class T>
106-
concept nullable = requires(T v, const T cv) {
107-
{ v.reset() } noexcept;
108-
{ cv.has_value() } noexcept -> std::same_as<bool>;
109-
};
110-
111110
template <class O, class Disc>
112111
struct invoker_base {
113112
invoker_base() = default;
@@ -141,112 +140,73 @@ struct invoker;
141140
PRO4D_DEF_OVERLOAD_SPECIALIZATIONS(PRO4D_DEF_INVOKER)
142141
#undef PRO4D_DEF_INVOKER
143142

144-
struct sentinel_meta {
145-
sentinel_meta() = default;
146-
template <class P>
147-
explicit sentinel_meta(std::in_place_type_t<P>) noexcept : v_(1) {}
148-
void reset() noexcept { v_ = 0; }
149-
bool has_value() const noexcept { return v_; }
150-
151-
private:
152-
std::ptrdiff_t v_;
153-
};
154-
155-
template <nullable First, class... Rest>
156-
struct PRO4D_ENFORCE_EBO inline_meta_storage : First, Rest... {
157-
using First::has_value;
158-
using First::reset;
159-
160-
constexpr inline_meta_storage() noexcept {}
161-
template <class P>
162-
constexpr explicit inline_meta_storage(std::in_place_type_t<P>)
163-
: First(std::in_place_type<P>), Rest(std::in_place_type<P>)... {}
164-
inline_meta_storage(const inline_meta_storage& rhs) noexcept
165-
: inline_meta_storage() {
166-
if (static_cast<const First&>(rhs).has_value()) {
167-
static_cast<First&>(*this) = static_cast<const First&>(rhs);
168-
((static_cast<Rest&>(*this) = static_cast<const Rest&>(rhs)), ...);
169-
} else {
170-
static_cast<First&>(*this).reset();
171-
}
172-
}
173-
inline_meta_storage& operator=(const inline_meta_storage& rhs) noexcept {
174-
if (static_cast<const First&>(rhs).has_value()) {
175-
static_cast<First&>(*this) = static_cast<const First&>(rhs);
176-
((static_cast<Rest&>(*this) = static_cast<const Rest&>(rhs)), ...);
177-
} else {
178-
static_cast<First&>(*this).reset();
179-
}
143+
template <class M>
144+
struct PRO4D_ENFORCE_EBO inplace_meta_storage : M {
145+
using M::M;
146+
147+
inplace_meta_storage() = default;
148+
inplace_meta_storage(const inplace_meta_storage&) = default;
149+
template <class M2>
150+
requires(std::is_nothrow_convertible_v<const M2&, const M&>)
151+
inplace_meta_storage(const inplace_meta_storage<M2>& rhs) noexcept
152+
: M(static_cast<const M&>(*rhs)) {}
153+
inplace_meta_storage& operator=(const inplace_meta_storage&) = default;
154+
template <class M2>
155+
requires(std::is_nothrow_convertible_v<const M2&, const M&>)
156+
inplace_meta_storage&
157+
operator=(const inplace_meta_storage<M2>& rhs) noexcept {
158+
static_cast<M&>(*this) = static_cast<const M&>(*rhs);
180159
return *this;
181160
}
182-
template <class M>
183-
const M& get() const noexcept {
184-
return static_cast<const M&>(*this);
185-
}
186-
};
187-
template <nullable First>
188-
struct inline_meta_storage<First> : First {
189-
using First::First;
190161

191-
template <class M>
192-
const M& get() const noexcept {
193-
return static_cast<const M&>(*this);
194-
}
162+
const M& operator*() const noexcept { return *this; }
195163
};
196164

197-
template <class... Ms>
165+
template <class M>
198166
struct static_meta_storage {
199167
static_meta_storage() = default;
168+
template <class M2>
169+
requires(std::is_nothrow_convertible_v<const M2&, const M&>)
170+
static_meta_storage(const static_meta_storage<M2>& rhs) noexcept
171+
: ptr_(std::addressof(static_cast<const M&>(*rhs))) {}
172+
template <class M2>
173+
requires(std::is_nothrow_convertible_v<const M2&, const M&>)
174+
static_meta_storage& operator=(const static_meta_storage<M2>& rhs) noexcept {
175+
ptr_ = std::addressof(static_cast<const M&>(*rhs));
176+
return *this;
177+
}
200178
template <class P>
201179
explicit static_meta_storage(std::in_place_type_t<P>)
202180
: ptr_(std::addressof(storage<P>)) {}
203181
bool has_value() const noexcept { return ptr_ != nullptr; }
204182
void reset() noexcept { ptr_ = nullptr; }
205-
template <class M>
206-
const M& get() const noexcept {
207-
return (*ptr_).template get<M>();
208-
}
183+
const M& operator*() const noexcept { return *ptr_; }
209184

210185
private:
211-
meta_ptr<inline_meta_storage<Ms...>, void (*)(Ms...)> ptr_;
186+
meta_ptr<M, void (*)(M)> ptr_;
212187

213188
template <class P>
214-
static inline const inline_meta_storage<Ms...> storage{std::in_place_type<P>};
189+
static inline const M storage{std::in_place_type<P>};
215190
};
216191

217-
template <class... Ms>
218-
struct compact_meta_storage_traits
219-
: std::type_identity<static_meta_storage<Ms...>> {};
220-
template <nullable M>
221-
struct compact_meta_storage_traits<M>
222-
: std::type_identity<inline_meta_storage<M>> {};
223-
template <>
224-
struct compact_meta_storage_traits<>
225-
: std::type_identity<inline_meta_storage<sentinel_meta>> {};
226-
227-
template <class... Ms>
228-
struct flat_meta_storage_traits
229-
: std::type_identity<inline_meta_storage<sentinel_meta, Ms...>> {};
230-
template <nullable M, class... Ms>
231-
struct flat_meta_storage_traits<M, Ms...>
232-
: std::type_identity<inline_meta_storage<M, Ms...>> {};
233-
234192
} // namespace detail
235193

236194
struct compact_facade_meta_traits {
237195
template <class Ctx, class O>
238196
using invoker = detail::invoker<Ctx, O>;
239197

240-
template <class... Ms>
241-
using storage = detail::compact_meta_storage_traits<Ms...>::type;
198+
template <class M>
199+
using storage = std::conditional_t<sizeof(M) <= sizeof(void*),
200+
detail::inplace_meta_storage<M>,
201+
detail::static_meta_storage<M>>;
242202
};
243203

244204
struct flat_facade_meta_traits {
245205
template <class Ctx, class O>
246206
using invoker = detail::invoker<Ctx, O>;
247207

248-
template <class... Ms>
249-
using storage = detail::flat_meta_storage_traits<Ms...>::type;
208+
template <class M>
209+
using storage = detail::inplace_meta_storage<M>;
250210
};
251211

252212
} // namespace pro::inline v4

0 commit comments

Comments
 (0)