Skip to content

Commit 248e8cf

Browse files
authored
Fix the std::in_place_type_t overload for SFINAE-unsafe facades (#223)
1 parent 973b4b1 commit 248e8cf

File tree

4 files changed

+60
-50
lines changed

4 files changed

+60
-50
lines changed

.github/workflows/bvt-clang.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,14 @@ jobs:
1515

1616
- name: install clang
1717
run: |
18-
sudo apt install -y clang-15 clang-16 clang-17 clang-18
18+
sudo apt install -y clang-16 clang-17 clang-18
1919
2020
- name: check compiler versions
2121
run: |
22-
clang++-15 --version
2322
clang++-16 --version
2423
clang++-17 --version
2524
clang++-18 --version
2625
27-
- name: build and run test with clang 15
28-
run: |
29-
cmake -B build-clang-15 -DCMAKE_C_COMPILER=clang-15 -DCMAKE_CXX_COMPILER=clang++-15 -DCMAKE_BUILD_TYPE=Release
30-
cmake --build build-clang-15 -j
31-
ctest --test-dir build-clang-15 -j
32-
3326
- name: build and run test with clang 16
3427
run: |
3528
cmake -B build-clang-16 -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_BUILD_TYPE=Release

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ The "Proxy" library is a self-contained solution for runtime polymorphism in C++
167167
| Family | Minimum version | Required flags |
168168
| ---------- | --------------- | -------------- |
169169
| GCC | 13.1 | -std=c++20 |
170-
| Clang | 15.0.0 | -std=c++20 |
170+
| Clang | 16.0.0 | -std=c++20 |
171171
| MSVC | 19.31 | /std:c++20 |
172172
| NVIDIA HPC | 24.1 | -std=c++20 |
173173

proxy.h

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,12 @@ template <class A1, class A2>
520520
using merged_composite_accessor =
521521
typename composite_accessor_merge_traits<A1, A2>::type;
522522

523+
template <class T> struct in_place_type_traits : inapplicable_traits {};
524+
template <class T>
525+
struct in_place_type_traits<std::in_place_type_t<T>> : applicable_traits {};
526+
template <class T>
527+
constexpr bool is_in_place_type = in_place_type_traits<T>::applicable;
528+
523529
template <class F>
524530
consteval bool is_facade_constraints_well_formed() {
525531
if constexpr (requires {
@@ -782,14 +788,15 @@ class proxy : public details::facade_traits<F>::direct_accessor {
782788
}
783789
template <class P>
784790
proxy(P&& ptr) noexcept(std::is_nothrow_constructible_v<std::decay_t<P>, P>)
785-
requires(proxiable<std::decay_t<P>, F> &&
786-
std::is_constructible_v<std::decay_t<P>, P>) : proxy()
787-
{ initialize<std::decay_t<P>>(std::forward<P>(ptr)); }
791+
requires(!details::is_in_place_type<std::decay_t<P>> &&
792+
proxiable<std::decay_t<P>, F> &&
793+
std::is_constructible_v<std::decay_t<P>, P>)
794+
: proxy() { initialize<std::decay_t<P>>(std::forward<P>(ptr)); }
788795
template <proxiable<F> P, class... Args>
789796
explicit proxy(std::in_place_type_t<P>, Args&&... args)
790797
noexcept(std::is_nothrow_constructible_v<P, Args...>)
791-
requires(std::is_constructible_v<P, Args...>) : proxy()
792-
{ initialize<P>(std::forward<Args>(args)...); }
798+
requires(std::is_constructible_v<P, Args...>)
799+
: proxy() { initialize<P>(std::forward<Args>(args)...); }
793800
template <proxiable<F> P, class U, class... Args>
794801
explicit proxy(std::in_place_type_t<P>, std::initializer_list<U> il,
795802
Args&&... args)
@@ -1258,8 +1265,6 @@ template <class F, class C>
12581265
using adl_accessor_arg_t =
12591266
std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>;
12601267

1261-
template <class O>
1262-
using overload_return_type = typename overload_traits<O>::return_type;
12631268
#define ___PRO_DEF_CAST_ACCESSOR(Q, SELF, ...) \
12641269
template <class __F, class __C, class T> \
12651270
struct accessor<__F, __C, T() Q> { \
@@ -1274,7 +1279,7 @@ using overload_return_type = typename overload_traits<O>::return_type;
12741279
template <bool Expl, bool Nullable>
12751280
struct cast_dispatch_base {
12761281
___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_CAST_ACCESSOR,
1277-
operator overload_return_type<__Os>)
1282+
operator typename overload_traits<__Os>::return_type)
12781283
};
12791284
#undef ___PRO_DEF_CAST_ACCESSOR
12801285

@@ -1468,11 +1473,10 @@ struct observer_overload_mapping_traits<F, IS_DIRECT, D, O>
14681473
: observer_overload_mapping_traits_impl<F, IS_DIRECT, D, O> {};
14691474
template <class F, class O>
14701475
struct observer_overload_mapping_traits<F, true, upward_conversion_dispatch, O>
1471-
: std::type_identity<proxy_view<
1472-
std::conditional_t<std::is_const_v<F>,
1473-
const facade_of_t<typename overload_traits<O>::return_type>,
1474-
facade_of_t<typename overload_traits<O>::return_type>
1475-
>>() const noexcept> {};
1476+
: std::type_identity<proxy_view<std::conditional_t<std::is_const_v<F>,
1477+
const facade_of_t<typename overload_traits<O>::return_type>,
1478+
facade_of_t<typename overload_traits<O>::return_type>>>()
1479+
const noexcept> {};
14761480

14771481
template <class D>
14781482
struct observer_dispatch_reduction : std::type_identity<D> {};
@@ -1702,22 +1706,20 @@ struct basic_facade_builder {
17021706
using support_destruction = basic_facade_builder<
17031707
Cs, Rs, details::make_destructible(C, CL)>;
17041708
#ifdef __cpp_rtti
1705-
using support_indirect_rtti =
1706-
basic_facade_builder<
1707-
details::add_conv_t<Cs, details::conv_impl<false,
1708-
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
1709-
void(details::proxy_cast_context) const&,
1710-
void(details::proxy_cast_context) &&>>,
1711-
details::add_tuple_t<Rs, details::refl_impl<false,
1712-
details::proxy_typeid_reflector>>, C>;
1713-
using support_direct_rtti =
1714-
basic_facade_builder<
1715-
details::add_conv_t<Cs, details::conv_impl<true,
1716-
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
1717-
void(details::proxy_cast_context) const&,
1718-
void(details::proxy_cast_context) &&>>,
1719-
details::add_tuple_t<Rs, details::refl_impl<true,
1720-
details::proxy_typeid_reflector>>, C>;
1709+
using support_indirect_rtti = basic_facade_builder<
1710+
details::add_conv_t<Cs, details::conv_impl<false,
1711+
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
1712+
void(details::proxy_cast_context) const&,
1713+
void(details::proxy_cast_context) &&>>,
1714+
details::add_tuple_t<Rs, details::refl_impl<false,
1715+
details::proxy_typeid_reflector>>, C>;
1716+
using support_direct_rtti = basic_facade_builder<
1717+
details::add_conv_t<Cs, details::conv_impl<true,
1718+
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
1719+
void(details::proxy_cast_context) const&,
1720+
void(details::proxy_cast_context) &&>>,
1721+
details::add_tuple_t<Rs, details::refl_impl<true,
1722+
details::proxy_typeid_reflector>>, C>;
17211723
using support_rtti = support_indirect_rtti;
17221724
#endif // __cpp_rtti
17231725
template <class F>
@@ -1783,17 +1785,17 @@ struct operator_dispatch;
17831785
#define ___PRO_LHS_BINARY_OP_DISPATCH_BODY_IMPL(...) \
17841786
template <class T, class Arg> \
17851787
decltype(auto) operator()(T&& self, Arg&& arg) \
1786-
___PRO_DIRECT_FUNC_IMPL(std::forward<T>(self) __VA_ARGS__ \
1787-
std::forward<Arg>(arg))
1788+
___PRO_DIRECT_FUNC_IMPL( \
1789+
std::forward<T>(self) __VA_ARGS__ std::forward<Arg>(arg))
17881790
#define ___PRO_LHS_ALL_OP_DISPATCH_BODY_IMPL(...) \
17891791
___PRO_LHS_LEFT_OP_DISPATCH_BODY_IMPL(__VA_ARGS__) \
17901792
___PRO_LHS_BINARY_OP_DISPATCH_BODY_IMPL(__VA_ARGS__)
17911793
#define ___PRO_LHS_OP_DISPATCH_IMPL(TYPE, ...) \
17921794
template <> \
17931795
struct operator_dispatch<#__VA_ARGS__, false> { \
17941796
___PRO_LHS_##TYPE##_OP_DISPATCH_BODY_IMPL(__VA_ARGS__) \
1795-
___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_LHS_##TYPE##_OP_ACCESSOR, \
1796-
operator __VA_ARGS__) \
1797+
___PRO_DEF_MEM_ACCESSOR_TEMPLATE( \
1798+
___PRO_DEF_LHS_##TYPE##_OP_ACCESSOR, operator __VA_ARGS__) \
17971799
};
17981800

17991801
#define ___PRO_DEF_RHS_OP_ACCESSOR(Q, NE, SELF_ARG, SELF, ...) \
@@ -1817,10 +1819,10 @@ ___PRO_DEBUG( \
18171819
struct operator_dispatch<#__VA_ARGS__, true> { \
18181820
template <class T, class Arg> \
18191821
decltype(auto) operator()(T&& self, Arg&& arg) \
1820-
___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ \
1821-
std::forward<T>(self)) \
1822-
___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_RHS_OP_ACCESSOR, \
1823-
__VA_ARGS__) \
1822+
___PRO_DIRECT_FUNC_IMPL( \
1823+
std::forward<Arg>(arg) __VA_ARGS__ std::forward<T>(self)) \
1824+
___PRO_DEF_FREE_ACCESSOR_TEMPLATE( \
1825+
___PRO_DEF_RHS_OP_ACCESSOR, __VA_ARGS__) \
18241826
};
18251827

18261828
#define ___PRO_EXTENDED_BINARY_OP_DISPATCH_IMPL(...) \
@@ -1873,8 +1875,8 @@ ___PRO_DEBUG( \
18731875
struct operator_dispatch<#__VA_ARGS__, true> { \
18741876
template <class T, class Arg> \
18751877
decltype(auto) operator()(T&& self, Arg&& arg) \
1876-
___PRO_DIRECT_FUNC_IMPL(std::forward<Arg>(arg) __VA_ARGS__ \
1877-
std::forward<T>(self)) \
1878+
___PRO_DIRECT_FUNC_IMPL( \
1879+
std::forward<Arg>(arg) __VA_ARGS__ std::forward<T>(self)) \
18781880
___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_RHS_ASSIGNMENT_OP_ACCESSOR, \
18791881
__VA_ARGS__) \
18801882
};
@@ -1960,8 +1962,7 @@ struct implicit_conversion_dispatch
19601962
template <class T>
19611963
T&& operator()(T&& self) noexcept { return std::forward<T>(self); }
19621964
};
1963-
struct explicit_conversion_dispatch
1964-
: details::cast_dispatch_base<true, false> {
1965+
struct explicit_conversion_dispatch : details::cast_dispatch_base<true, false> {
19651966
template <class T>
19661967
auto operator()(T&& self) noexcept
19671968
{ return details::explicit_conversion_adapter<T>{std::forward<T>(self)}; }

tests/proxy_creation_tests.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ static_assert(!pro::inplace_proxiable_target<utils::LifetimeTracker::Session, Te
8080
static_assert(!noexcept(pro::make_proxy_inplace<TestLargeStringable, utils::LifetimeTracker::Session>(std::declval<utils::LifetimeTracker*>())));
8181
static_assert(noexcept(pro::make_proxy_inplace<TestLargeStringable, int>(123)));
8282

83+
template <class T>
84+
void SfinaeUnsafeIncrementImpl(T&& value) { ++value; }
85+
86+
PRO_DEF_FREE_DISPATCH(FreeSfinaeUnsafeIncrement, SfinaeUnsafeIncrementImpl, Increment);
87+
88+
struct SfinaeUnsafeFacade : pro::facade_builder
89+
::support_rtti
90+
::add_convention<FreeSfinaeUnsafeIncrement, void()>
91+
::build {};
92+
8393
} // namespace proxy_creation_tests_details
8494

8595
namespace details = proxy_creation_tests_details;
@@ -535,3 +545,9 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Move) {
535545
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
536546
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
537547
}
548+
549+
TEST(ProxyCreationTests, TestMakeProxy_SfinaeUnsafe) {
550+
pro::proxy<details::SfinaeUnsafeFacade> p = pro::make_proxy<details::SfinaeUnsafeFacade, int>();
551+
Increment(*p);
552+
ASSERT_EQ(proxy_cast<int>(*p), 1);
553+
}

0 commit comments

Comments
 (0)