Repository navigation
fix(dsl): classify callable handlers without instantiating them - #61
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Detect a call operator by deriving from the handler type and a class with a known operator(), instead of taking the address of operator() with explicit template arguments. The old probe instantiated the body of generic lambdas with int arguments, so a generic lambda that accesses members of its argument failed to compile. Pointers to members are handlers as well now, so `>> &Circle::area` calls the member instead of returning the pointer. Handlers that return a reference (a lambda returning `const T &`, a pointer to data member) were rejected by the invocability check and are accepted now.
f6a097a to
ebaf51a
Compare
Benchmark comparison (informational)Base Noise estimate: the 20 control rows below do not run Patternia code, so they should not change between base and PR. As a group they moved by a median of +0.0% (run order and machine drift); that offset is divided out of the Patternia deltas (Adjusted). The controls then scatter by a median of 1.4%. Adjusted differences under 5.0% are treated as noise. Patternia: 8 benchmarks, 0 slower, 0 faster, 8 within noise.
Control rows (not Patternia code)
Raw results and the comparison chart are attached to this workflow run as an artifact. Shared runners are noisy, so use this as a trend, not a measurement. |
Summary
Handler classification decided whether a handler is callable by instantiating its
operator()withintarguments. That broke generic lambdas whose body only compiles for their real argument type, treated pointers to members as plain values, and the invocability check rejected any handler that returns a reference. This change fixes all three, which together makepattern >> &Circle::areaand$ >> &Point::ywork.Changes
common_traits.hpp: detect a call operator without instantiating any body, by deriving from the handler type and from a class with a knownoperator(); an ambiguousoperator()means the handler type has one. This works for plain, overloaded and generic call operators.finalclass cannot be derived from, so for it only a single non-templateoperator()is recognised, which keeps non-template final function objects working.pattern >> &T::memberused to return the member pointer itself as a value, and now calls the member on the bound value.is_applicable_impltakes the result type throughremove_reference_t, so handlers returning a reference (a lambda returningconst T &, a pointer to data member) are no longer rejected with "Handler signature does not match".tests/tests_handler_forms.cppwith 8 cases, includingstatic_asserts on the classification trait, registered intests/CMakeLists.txt.Testing
static_asserts and the "Handler signature does not match" diagnostic), so the tests do exercise the change.common_traits.hpp(get_case_result_impl,get_otherwise_result_impl) comes from running clang-format on the whole file, which the CI format check requires once a file is touched.finalclass with a template or overloadedoperator()is still classified as a value, andstd::mem_fn(&T::m)as a handler still fails with libc++ (not investigated); use the member pointer directly.