Skip to content

Commit 6446f9c

Browse files
authored
reduce use of SFINAE to shorten error messages (#44)
Remove SFINAE from user-facing functions to reduce the length of compiler errors. For example, the error for `eq(a == b)` instead of `eq(a, b)` is <details><summary>before</summary> ``` external/toolchains_llvm++llvm+llvm_toolchain_llvm/bin/../include/c++/v1/__type_traits/invoke.h:314:1: error: no type named 'type' in 'std::invoke_result<const (lambda at sel/test/constant_test.cpp:26:7) &, int>' 314 | using invoke_result_t = typename invoke_result<_Fn, _Args...>::type; | ^~~~~ external/skytest+/src/test.hpp:27:16: note: in instantiation of template type alias 'invoke_result_t' requested here 27 | std::invoke_result_t<const remove_cvref_t<F>&, Args...>, | ^ external/toolchains_llvm++llvm+llvm_toolchain_llvm/bin/../include/c++/v1/__type_traits/conjunction.h:60:58: note: in instantiation of template class 'skytest::detail::returns_result<(lambda at sel/test/constant_test.cpp:26:7), int>' requested here 60 | struct conjunction<_Arg, _Args...> : conditional_t<!bool(_Arg::value), _Arg, conjunction<_Args...>> {}; | ^ external/skytest+/src/test_param.hpp:117:7: note: in instantiation of template class 'std::conjunction<skytest::detail::returns_result<(lambda at sel/test/constant_test.cpp:26:7), int>, skytest::detail::returns_result<(lambda at sel/test/constant_test.cpp:26:7), float>, skytest::detail::returns_result<(lambda at sel/test/constant_test.cpp:26:7), double>>' requested here 117 | : std::conjunction<returns_result<F, param_reference_t<Is, Params>>...> | ^ external/skytest+/src/test_param.hpp:127:7: note: in instantiation of template class 'skytest::detail::param_invocable_<std::integer_sequence<unsigned long, 0, 1, 2>, (lambda at sel/test/constant_test.cpp:26:7), skytest::constexpr_params_t<1, 2.000000e+00, 3.000000e+00>>' requested here 127 | : param_invocable_<param_sequence_t<Params>, F, param_resolve_t<Params>> | ^ external/skytest+/src/test_param.hpp:131:43: note: in instantiation of template class 'skytest::detail::param_invocable<(lambda at sel/test/constant_test.cpp:26:7), skytest::param_ref_t<skytest::constexpr_params_instance>>' requested here 131 | inline constexpr auto param_invocable_v = param_invocable<F, Params>::value; | ^ external/skytest+/src/test_param.hpp:252:11: note: in instantiation of variable template specialization 'skytest::detail::param_invocable_v' requested here 252 | param_invocable_v<F, params_type> and | ^ external/skytest+/src/test_param.hpp:256:8: note: while substituting prior template arguments into non-type template parameter [with F = (lambda at sel/test/constant_test.cpp:26:7)] 256 | auto operator=(const F& func) && -> void | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 257 | { | ~ 258 | assign_impl( | ~~~~~~~~~~~~ 259 | param_sequence_t<params_type>{}, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 260 | param_bound_closure<F, params_type>{func, params_}); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 261 | } | ~ sel/test/constant_test.cpp:25:38: note: while substituting deduced template arguments into function template 'operator=' [with F = (lambda at sel/test/constant_test.cpp:26:7), $1 = (no value)] 25 | constexpr_params<1, 2.0F, 3.0> = // | ^ sel/test/constant_test.cpp:25:38: error: no viable overloaded '=' 24 | "constants always define a strong ordering"_ctest * | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 25 | constexpr_params<1, 2.0F, 3.0> = // | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ 26 | []<class T>(T value) { | ~~~~~~~~~~~~~~~~~~~~~~ 27 | auto a = sel::constant{value}; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 | auto b = sel::constant{T{2} * value}; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 29 | return expect( | ~~~~~~~~~~~~~~ 30 | eq(std::strong_ordering::less == (a <=> b)) and // | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 | eq(std::strong_ordering::greater, (b <=> a)) and // | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | eq(std::strong_ordering::equal, (b <=> b)) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | ); | ~~ 34 | }; | ~ external/skytest+/src/test_param.hpp:256:8: note: candidate template ignored: substitution failure [with F = (lambda at sel/test/constant_test.cpp:26:7)] 256 | auto operator=(const F& func) && -> void | ^ external/skytest+/src/test_param.hpp:269:8: note: candidate template ignored: substitution failure [with F = (lambda at sel/test/constant_test.cpp:26:7)] 269 | auto operator=(const F& func) && -> void | ^ external/skytest+/src/test_param.hpp:174:7: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from '(lambda at sel/test/constant_test.cpp:26:7)' to 'const parameterized_test<param_ref_t<skytest::constexpr_params_instance>, compile_time>' for 1st argument 174 | class parameterized_test | ^~~~~~~~~~~~~~~~~~ 3 errors generated. ``` </details> <details><summary>after</summary> ``` external/skytest+/src/detail/predicate.hpp:35:18: error: no matching function for call to object of type 'const std::equal_to<void>' 35 | auto value = static_cast<const F&>(*this)(std::as_const(args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ sel/test/constant_test.cpp:30:13: note: in instantiation of function template specialization 'skytest::detail::predicate<std::equal_to<void>, skytest::detail::type_name, skytest::detail::pred_fmt::eq_>::operator()<bool>' requested here 30 | eq(std::strong_ordering::less == (a <=> b)) and // | ^ external/skytest+/src/test_param.hpp:161:26: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<double>' requested here 161 | return [] { return func(get<I>(params)); }; | ^ external/skytest+/src/test_param.hpp:161:17: note: while substituting into a lambda expression here 161 | return [] { return func(get<I>(params)); }; | ^ external/skytest+/src/test_param.hpp:231:15: note: in instantiation of function template specialization 'skytest::detail::param_bound_static_closure<f, skytest::constexpr_params_instance>::operator[]<2UL>' requested here 231 | g[constant<Is>{}], | ^ external/skytest+/src/test_param.hpp:253:7: note: in instantiation of function template specialization 'skytest::detail::parameterized_test<skytest::param_ref_t<skytest::constexpr_params_instance>, skytest::detail::test_style::compile_time>::assign_impl<0UL, 1UL, 2UL, skytest::detail::param_bound_static_closure<f, skytest::constexpr_params_instance>>' requested here 253 | assign_impl( | ^ sel/test/constant_test.cpp:25:38: note: in instantiation of function template specialization 'skytest::detail::parameterized_test<skytest::param_ref_t<skytest::constexpr_params_instance>, skytest::detail::test_style::compile_time>::operator=<(lambda at sel/test/constant_test.cpp:26:7)>' requested here 25 | constexpr_params<1, 2.0F, 3.0> = // | ^ external/toolchains_llvm++llvm+llvm_toolchain_llvm/bin/../include/c++/v1/__functional/operations.h:308:60: note: candidate function template not viable: requires 2 arguments, but 1 was provided 308 | _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI auto operator()(_T1&& __t, _T2&& __u) const | ^ ~~~~~~~~~~~~~~~~~~~~ 1 error generated. ``` </details> Change-Id: Ibc7ab9d85dc732ae7b895093147584211ef713ac
1 parent e1ba769 commit 6446f9c

4 files changed

Lines changed: 34 additions & 51 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ results in the follow build error (snippet):
9494
external/llvm_toolchain_llvm/bin/../include/c++/v1/__functional/operations.h:374:37: note: read of non-const variable 'n' is not allowed in a constant expression
9595
374 | return std::forward<_T1>(__t) < std::forward<_T2>(__u);
9696
| ^
97-
./src/detail/predicate.hpp:38:24: note: in call to 'static_cast<const std::less<void> &>(*this).operator()<const int &, const int &>(0, n)'
98-
38 | const auto value = static_cast<const F&>(*this)(std::as_const(args)...);
99-
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97+
./src/detail/predicate.hpp:35:18: note: in call to 'static_cast<const std::less<void> &>(*this).operator()<const int &, const int &>(0, n)'
98+
35 | auto value = static_cast<const F&>(*this)(std::as_const(args)...);
99+
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
100100
example/ctest_fail.cpp:10:47: note: (skipping 2 calls in backtrace; use -fconstexpr-backtrace-limit=0 to see all)
101101
10 | "read non-const"_ctest = [] { return expect(lt(0, n)); };
102102
| ^

example/ctest_fail.log

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
external/llvm_toolchain_llvm/bin/../include/c++/v1/__functional/operations.h:374:37: note: read of non-const variable 'n' is not allowed in a constant expression
1111
374 | return std::forward<_T1>(__t) < std::forward<_T2>(__u);
1212
| ^
13-
./src/detail/predicate.hpp:38:24: note: in call to 'static_cast<const std::less<void> &>(*this).operator()<const int &, const int &>(0, n)'
14-
38 | const auto value = static_cast<const F&>(*this)(std::as_const(args)...);
15-
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
./src/detail/predicate.hpp:35:18: note: in call to 'static_cast<const std::less<void> &>(*this).operator()<const int &, const int &>(0, n)'
14+
35 | auto value = static_cast<const F&>(*this)(std::as_const(args)...);
15+
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1616
example/ctest_fail.cpp:10:47: note: (skipping 2 calls in backtrace; use -fconstexpr-backtrace-limit=0 to see all)
1717
10 | "read non-const"_ctest = [] { return expect(lt(0, n)); };
1818
| ^

src/detail/predicate.hpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@ struct predicate : F
2929
constexpr predicate(T&& f) : F{std::forward<T>(f)}
3030
{}
3131

32-
template <
33-
class... Ts,
34-
class =
35-
std::enable_if_t<std::is_invocable_r_v<bool, const F&, const Ts&...>>>
32+
template <class... Ts>
3633
constexpr auto operator()(Ts&&... args) const
3734
{
38-
const auto value = static_cast<const F&>(*this)(std::as_const(args)...);
35+
auto value = static_cast<const F&>(*this)(std::as_const(args)...);
36+
3937
return relation<predicate, std::decay_t<Ts>...>{
40-
std::tuple<std::decay_t<Ts>...>{std::forward<Ts>(args)...}, value};
38+
std::tuple<std::decay_t<Ts>...>{std::forward<Ts>(args)...},
39+
std::move(value)};
4140
}
4241
};
4342

src/test_param.hpp

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -152,21 +152,14 @@ struct param_bound_closure
152152
template <const auto& func, const auto& params>
153153
struct param_bound_static_closure
154154
{
155-
template <
156-
std::size_t I,
157-
class P = remove_cvref_t<decltype(params)>,
158-
std::enable_if_t<not is_range_v<P>, bool> = true>
159-
constexpr auto operator[](constant<I>) const
160-
{
161-
return [] { return func(get<I>(params)); };
162-
}
163-
template <
164-
std::size_t I,
165-
class P = remove_cvref_t<decltype(params)>,
166-
std::enable_if_t<is_range_v<P>, bool> = true>
155+
template <std::size_t I>
167156
constexpr auto operator[](constant<I>) const
168157
{
169-
return [] { return func(params.begin()[I]); };
158+
if constexpr (is_range_v<decltype(params)>) {
159+
return [] { return func(params.begin()[I]); };
160+
} else {
161+
return [] { return func(get<I>(params)); };
162+
}
170163
}
171164
};
172165

@@ -246,34 +239,25 @@ class parameterized_test
246239
: params_{params}, basename_{basename}
247240
{}
248241

249-
template <
250-
class F,
251-
std::enable_if_t<
252-
param_invocable_v<F, params_type> and
253-
not(is_static_closure_constructible_v<F> and
254-
has_static_value_v<params_type>),
255-
bool> = true>
242+
template <class F>
256243
auto operator=(const F& func) && -> void
257244
{
258-
assign_impl(
259-
param_sequence_t<params_type>{},
260-
param_bound_closure<F, params_type>{func, params_});
261-
}
262-
template <
263-
class F,
264-
std::enable_if_t<
265-
param_invocable_v<F, params_type> and
266-
is_static_closure_constructible_v<F> and
267-
has_static_value_v<params_type>,
268-
bool> = true>
269-
auto operator=(const F& func) && -> void
270-
{
271-
static const auto f = func;
272-
static constexpr const auto& p = params_type::value;
273-
274-
assign_impl(
275-
std::make_index_sequence<static_size_v<params_type>>{},
276-
param_bound_static_closure<f, p>{});
245+
constexpr auto is_constexpr_invocable =
246+
is_static_closure_constructible_v<F> and
247+
has_static_value_v<params_type>;
248+
249+
if constexpr (is_constexpr_invocable) {
250+
static const auto f = func;
251+
static constexpr const auto& p = params_type::value;
252+
253+
assign_impl(
254+
std::make_index_sequence<static_size_v<params_type>>{},
255+
param_bound_static_closure<f, p>{});
256+
} else {
257+
assign_impl(
258+
param_sequence_t<params_type>{},
259+
param_bound_closure<F, params_type>{func, params_});
260+
}
277261
}
278262
};
279263

0 commit comments

Comments
 (0)