Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .github/workflows/bvt-clang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,14 @@ jobs:

- name: install clang
run: |
sudo apt install -y clang-15 clang-16 clang-17 clang-18
sudo apt install -y clang-16 clang-17 clang-18

- name: check compiler versions
run: |
clang++-15 --version
clang++-16 --version
clang++-17 --version
clang++-18 --version

- name: build and run test with clang 15
run: |
cmake -B build-clang-15 -DCMAKE_C_COMPILER=clang-15 -DCMAKE_CXX_COMPILER=clang++-15 -DCMAKE_BUILD_TYPE=Release
cmake --build build-clang-15 -j
ctest --test-dir build-clang-15 -j

- name: build and run test with clang 16
run: |
cmake -B build-clang-16 -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_BUILD_TYPE=Release
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ The "Proxy" library is a self-contained solution for runtime polymorphism in C++
| Family | Minimum version | Required flags |
| ---------- | --------------- | -------------- |
| GCC | 13.1 | -std=c++20 |
| Clang | 15.0.0 | -std=c++20 |
| Clang | 16.0.0 | -std=c++20 |
| MSVC | 19.31 | /std:c++20 |
| NVIDIA HPC | 24.1 | -std=c++20 |

Expand Down
83 changes: 42 additions & 41 deletions proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,12 @@ template <class A1, class A2>
using merged_composite_accessor =
typename composite_accessor_merge_traits<A1, A2>::type;

template <class T> struct in_place_type_traits : inapplicable_traits {};
template <class T>
struct in_place_type_traits<std::in_place_type_t<T>> : applicable_traits {};
template <class T>
constexpr bool is_in_place_type = in_place_type_traits<T>::applicable;

template <class F>
consteval bool is_facade_constraints_well_formed() {
if constexpr (requires {
Expand Down Expand Up @@ -782,14 +788,15 @@ class proxy : public details::facade_traits<F>::direct_accessor {
}
template <class P>
proxy(P&& ptr) noexcept(std::is_nothrow_constructible_v<std::decay_t<P>, P>)
requires(proxiable<std::decay_t<P>, F> &&
std::is_constructible_v<std::decay_t<P>, P>) : proxy()
{ initialize<std::decay_t<P>>(std::forward<P>(ptr)); }
requires(!details::is_in_place_type<std::decay_t<P>> &&
proxiable<std::decay_t<P>, F> &&
std::is_constructible_v<std::decay_t<P>, P>)
: proxy() { initialize<std::decay_t<P>>(std::forward<P>(ptr)); }
template <proxiable<F> P, class... Args>
explicit proxy(std::in_place_type_t<P>, Args&&... args)
noexcept(std::is_nothrow_constructible_v<P, Args...>)
requires(std::is_constructible_v<P, Args...>) : proxy()
{ initialize<P>(std::forward<Args>(args)...); }
requires(std::is_constructible_v<P, Args...>)
: proxy() { initialize<P>(std::forward<Args>(args)...); }
template <proxiable<F> P, class U, class... Args>
explicit proxy(std::in_place_type_t<P>, std::initializer_list<U> il,
Args&&... args)
Expand Down Expand Up @@ -1258,8 +1265,6 @@ template <class F, class C>
using adl_accessor_arg_t =
std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>;

template <class O>
using overload_return_type = typename overload_traits<O>::return_type;
#define ___PRO_DEF_CAST_ACCESSOR(Q, SELF, ...) \
template <class __F, class __C, class T> \
struct accessor<__F, __C, T() Q> { \
Expand All @@ -1274,7 +1279,7 @@ using overload_return_type = typename overload_traits<O>::return_type;
template <bool Expl, bool Nullable>
struct cast_dispatch_base {
___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_CAST_ACCESSOR,
operator overload_return_type<__Os>)
operator typename overload_traits<__Os>::return_type)
};
#undef ___PRO_DEF_CAST_ACCESSOR

Expand Down Expand Up @@ -1468,11 +1473,10 @@ struct observer_overload_mapping_traits<F, IS_DIRECT, D, O>
: observer_overload_mapping_traits_impl<F, IS_DIRECT, D, O> {};
template <class F, class O>
struct observer_overload_mapping_traits<F, true, upward_conversion_dispatch, O>
: std::type_identity<proxy_view<
std::conditional_t<std::is_const_v<F>,
const facade_of_t<typename overload_traits<O>::return_type>,
facade_of_t<typename overload_traits<O>::return_type>
>>() const noexcept> {};
: std::type_identity<proxy_view<std::conditional_t<std::is_const_v<F>,
const facade_of_t<typename overload_traits<O>::return_type>,
facade_of_t<typename overload_traits<O>::return_type>>>()
const noexcept> {};

template <class D>
struct observer_dispatch_reduction : std::type_identity<D> {};
Expand Down Expand Up @@ -1702,22 +1706,20 @@ struct basic_facade_builder {
using support_destruction = basic_facade_builder<
Cs, Rs, details::make_destructible(C, CL)>;
#ifdef __cpp_rtti
using support_indirect_rtti =
basic_facade_builder<
details::add_conv_t<Cs, details::conv_impl<false,
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
void(details::proxy_cast_context) const&,
void(details::proxy_cast_context) &&>>,
details::add_tuple_t<Rs, details::refl_impl<false,
details::proxy_typeid_reflector>>, C>;
using support_direct_rtti =
basic_facade_builder<
details::add_conv_t<Cs, details::conv_impl<true,
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
void(details::proxy_cast_context) const&,
void(details::proxy_cast_context) &&>>,
details::add_tuple_t<Rs, details::refl_impl<true,
details::proxy_typeid_reflector>>, C>;
using support_indirect_rtti = basic_facade_builder<
details::add_conv_t<Cs, details::conv_impl<false,
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
void(details::proxy_cast_context) const&,
void(details::proxy_cast_context) &&>>,
details::add_tuple_t<Rs, details::refl_impl<false,
details::proxy_typeid_reflector>>, C>;
using support_direct_rtti = basic_facade_builder<
details::add_conv_t<Cs, details::conv_impl<true,
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
void(details::proxy_cast_context) const&,
void(details::proxy_cast_context) &&>>,
details::add_tuple_t<Rs, details::refl_impl<true,
details::proxy_typeid_reflector>>, C>;
using support_rtti = support_indirect_rtti;
#endif // __cpp_rtti
template <class F>
Expand Down Expand Up @@ -1783,17 +1785,17 @@ struct operator_dispatch;
#define ___PRO_LHS_BINARY_OP_DISPATCH_BODY_IMPL(...) \
template <class T, class Arg> \
decltype(auto) operator()(T&& self, Arg&& arg) \
___PRO_DIRECT_FUNC_IMPL(std::forward<T>(self) __VA_ARGS__ \
std::forward<Arg>(arg))
___PRO_DIRECT_FUNC_IMPL( \
std::forward<T>(self) __VA_ARGS__ std::forward<Arg>(arg))
#define ___PRO_LHS_ALL_OP_DISPATCH_BODY_IMPL(...) \
___PRO_LHS_LEFT_OP_DISPATCH_BODY_IMPL(__VA_ARGS__) \
___PRO_LHS_BINARY_OP_DISPATCH_BODY_IMPL(__VA_ARGS__)
#define ___PRO_LHS_OP_DISPATCH_IMPL(TYPE, ...) \
template <> \
struct operator_dispatch<#__VA_ARGS__, false> { \
___PRO_LHS_##TYPE##_OP_DISPATCH_BODY_IMPL(__VA_ARGS__) \
___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_LHS_##TYPE##_OP_ACCESSOR, \
operator __VA_ARGS__) \
___PRO_DEF_MEM_ACCESSOR_TEMPLATE( \
___PRO_DEF_LHS_##TYPE##_OP_ACCESSOR, operator __VA_ARGS__) \
};

#define ___PRO_DEF_RHS_OP_ACCESSOR(Q, NE, SELF_ARG, SELF, ...) \
Expand All @@ -1817,10 +1819,10 @@ ___PRO_DEBUG( \
struct operator_dispatch<#__VA_ARGS__, true> { \
template <class T, class Arg> \
decltype(auto) operator()(T&& self, Arg&& arg) \
___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ \
std::forward<T>(self)) \
___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_RHS_OP_ACCESSOR, \
__VA_ARGS__) \
___PRO_DIRECT_FUNC_IMPL( \
std::forward<Arg>(arg) __VA_ARGS__ std::forward<T>(self)) \
___PRO_DEF_FREE_ACCESSOR_TEMPLATE( \
___PRO_DEF_RHS_OP_ACCESSOR, __VA_ARGS__) \
};

#define ___PRO_EXTENDED_BINARY_OP_DISPATCH_IMPL(...) \
Expand Down Expand Up @@ -1873,8 +1875,8 @@ ___PRO_DEBUG( \
struct operator_dispatch<#__VA_ARGS__, true> { \
template <class T, class Arg> \
decltype(auto) operator()(T&& self, Arg&& arg) \
___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ \
std::forward<T>(self)) \
___PRO_DIRECT_FUNC_IMPL( \
std::forward<Arg>(arg) __VA_ARGS__ std::forward<T>(self)) \
___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_RHS_ASSIGNMENT_OP_ACCESSOR, \
__VA_ARGS__) \
};
Expand Down Expand Up @@ -1960,8 +1962,7 @@ struct implicit_conversion_dispatch
template <class T>
T&& operator()(T&& self) noexcept { return std::forward<T>(self); }
};
struct explicit_conversion_dispatch
: details::cast_dispatch_base<true, false> {
struct explicit_conversion_dispatch : details::cast_dispatch_base<true, false> {
template <class T>
auto operator()(T&& self) noexcept
{ return details::explicit_conversion_adapter<T>{std::forward<T>(self)}; }
Expand Down
16 changes: 16 additions & 0 deletions tests/proxy_creation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ static_assert(!pro::inplace_proxiable_target<utils::LifetimeTracker::Session, Te
static_assert(!noexcept(pro::make_proxy_inplace<TestLargeStringable, utils::LifetimeTracker::Session>(std::declval<utils::LifetimeTracker*>())));
static_assert(noexcept(pro::make_proxy_inplace<TestLargeStringable, int>(123)));

template <class T>
void SfinaeUnsafeIncrementImpl(T&& value) { ++value; }

PRO_DEF_FREE_DISPATCH(FreeSfinaeUnsafeIncrement, SfinaeUnsafeIncrementImpl, Increment);

struct SfinaeUnsafeFacade : pro::facade_builder
::support_rtti
::add_convention<FreeSfinaeUnsafeIncrement, void()>
::build {};

} // namespace proxy_creation_tests_details

namespace details = proxy_creation_tests_details;
Expand Down Expand Up @@ -535,3 +545,9 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Move) {
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}

TEST(ProxyCreationTests, TestMakeProxy_SfinaeUnsafe) {
pro::proxy<details::SfinaeUnsafeFacade> p = pro::make_proxy<details::SfinaeUnsafeFacade, int>();
Increment(*p);
ASSERT_EQ(proxy_cast<int>(*p), 1);
}