Skip to content

Commit c8f0b6b

Browse files
authored
Add support for accessing the underlying pointer type via a dispatch (#85)
1 parent e650e70 commit c8f0b6b

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

Diff for: proxy.h

+20-7
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ template <class D, class T, bool NE, class R, class... Args>
152152
concept invocable_dispatch = requires { typename D::template invoker<T>; } &&
153153
is_invoker_well_formed<
154154
typename D::template invoker<T>, T, NE, R, Args...>();
155+
template <class D, class P, bool NE, class R, class... Args>
156+
concept invocable_dispatch_ptr = invocable_dispatch<
157+
D, typename ptr_traits<P>::target_type, NE, R, Args...> ||
158+
invocable_dispatch<D, const P, NE, R, Args...> ||
159+
invocable_dispatch<D, void, NE, R, Args...>;
155160

156161
template <bool NE, class R, class... Args>
157162
using func_ptr_t = std::conditional_t<
@@ -166,14 +171,20 @@ R invoke_dispatch(Args&&... args) {
166171
}
167172
}
168173
template <class P, class F, class R, class... Args>
169-
R invocation_dispatcher(const char* self, Args... args)
174+
R invocation_dispatcher_ref(const char* self, Args... args)
170175
noexcept(is_invoker_well_formed<
171176
F, typename ptr_traits<P>::target_type, true, R, Args...>()) {
172177
return invoke_dispatch<F, R>(ptr_traits<P>::dereference(*std::launder(
173178
reinterpret_cast<const P*>(self))), std::forward<Args>(args)...);
174179
}
180+
template <class P, class F, class R, class... Args>
181+
R invocation_dispatcher_ptr(const char* self, Args... args)
182+
noexcept(is_invoker_well_formed<F, const P, true, R, Args...>()) {
183+
return invoke_dispatch<F, R>(*std::launder(reinterpret_cast<const P*>(self)),
184+
std::forward<Args>(args)...);
185+
}
175186
template <class F, class R, class... Args>
176-
R invocation_default_dispatcher(const char*, Args... args)
187+
R invocation_dispatcher_void(const char*, Args... args)
177188
noexcept(is_invoker_well_formed<F, void, true, R, Args...>())
178189
{ return invoke_dispatch<F, R>(std::forward<Args>(args)...); }
179190
template <class P>
@@ -208,19 +219,21 @@ struct overload_traits_impl : applicable_traits {
208219
static constexpr func_ptr_t<NE, R, const char*, Args...> get() {
209220
if constexpr (invocable_dispatch<
210221
D, typename ptr_traits<P>::target_type, NE, R, Args...>) {
211-
return &invocation_dispatcher<P, typename D::template invoker<
222+
return &invocation_dispatcher_ref<P, typename D::template invoker<
212223
typename ptr_traits<P>::target_type>, R, Args...>;
224+
} else if constexpr (invocable_dispatch<D, const P, NE, R, Args...>) {
225+
return &invocation_dispatcher_ptr<
226+
P, typename D::template invoker<const P>, R, Args...>;
213227
} else {
214-
return &invocation_default_dispatcher<
228+
return &invocation_dispatcher_void<
215229
typename D::template invoker<void>, R, Args...>;
216230
}
217231
}
218232
};
219233
struct resolver { func_ptr_t<NE, R, Args...> operator()(Args...); };
220234
template <class D, class P>
221-
static constexpr bool applicable_ptr = invocable_dispatch<
222-
D, typename ptr_traits<P>::target_type, NE, R, Args...> ||
223-
invocable_dispatch<D, void, NE, R, Args...>;
235+
static constexpr bool applicable_ptr =
236+
invocable_dispatch_ptr<D, P, NE, R, Args...>;
224237
static constexpr bool is_noexcept = NE;
225238
};
226239
template <class O> struct overload_traits : inapplicable_traits {};

Diff for: tests/proxy_invocation_tests.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <typeinfo>
1212
#include <vector>
1313
#include "proxy.h"
14+
#include "utils.h"
1415

1516
namespace {
1617

@@ -50,6 +51,25 @@ PRO_DEF_FREE_DISPATCH(Append, AppendImpl, pro::proxy<Container<T>>(T));
5051
PRO_DEF_MEMBER_DISPATCH_WITH_DEFAULT(WeakAt, at, NotImplemented<std::string>, std::string(int));
5152
PRO_DEF_FACADE(ResourceDictionary, WeakAt);
5253

54+
template <class F, class T>
55+
pro::proxy<F> LockImpl(const std::weak_ptr<T>& p) {
56+
auto result = p.lock();
57+
if (static_cast<bool>(result)) {
58+
return result;
59+
}
60+
return nullptr;
61+
}
62+
template <class F>
63+
PRO_DEF_FREE_DISPATCH(Lock, LockImpl<F>, pro::proxy<F>());
64+
template <class F>
65+
PRO_DEF_FACADE(Weak, Lock<F>, pro::copyable_ptr_constraints);
66+
template <class F, class T>
67+
auto GetWeakImpl(const std::shared_ptr<T>& p) { return pro::make_proxy<Weak<F>, std::weak_ptr<T>>(p); }
68+
template <class F>
69+
PRO_DEF_FREE_DISPATCH_WITH_DEFAULT(GetWeak, GetWeakImpl<F>, std::nullptr_t, pro::proxy<Weak<F>>());
70+
71+
PRO_DEF_FACADE(SharedStringable, PRO_MAKE_DISPATCH_PACK(utils::spec::ToString, GetWeak<SharedStringable>), pro::copyable_ptr_constraints);
72+
5373
} // namespace spec
5474

5575
template <class F, class D, bool NE, class... Args>
@@ -237,3 +257,20 @@ TEST(ProxyInvocationTests, TestFreeDispatchDefault) {
237257
ASSERT_TRUE(exception_thrown);
238258
}
239259
}
260+
261+
TEST(ProxyInvocationTests, TestObserverDispatch) {
262+
int test_val = 123;
263+
pro::proxy<spec::SharedStringable> p{std::make_shared<int>(test_val)};
264+
auto weak = p.invoke<spec::GetWeak<spec::SharedStringable>>();
265+
ASSERT_TRUE(weak.has_value());
266+
{
267+
auto locked = weak();
268+
ASSERT_TRUE(locked.has_value());
269+
ASSERT_EQ(locked.invoke<utils::spec::ToString>(), "123");
270+
}
271+
p = &test_val; // The underlying std::shared_ptr will be destroyed
272+
ASSERT_TRUE(weak.has_value());
273+
ASSERT_FALSE(weak().has_value());
274+
ASSERT_FALSE(p.invoke<spec::GetWeak<spec::SharedStringable>>().has_value());
275+
ASSERT_EQ(p.invoke<utils::spec::ToString>(), "123");
276+
}

0 commit comments

Comments
 (0)