mimic++ v10.0.1
Loading...
Searching...
No Matches
GeneralMatchers.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024-2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_MATCHERS_GENERAL_MATCHERS_HPP
7#define MIMICPP_MATCHERS_GENERAL_MATCHERS_HPP
8
9#pragma once
10
11#include "mimic++/Fwd.hpp"
19
20#ifndef MIMICPP_DETAIL_IS_MODULE
21 #include <array>
22 #include <any>
23 #include <functional>
24 #include <tuple>
25 #include <type_traits>
26 #include <utility>
27#endif
28
29namespace mimicpp::detail
30{
31 template <typename Arg, typename MatchesProjection = std::identity, typename DescribeProjection = printing::PrintFn>
32 struct arg_storage
33 {
34 using matches_reference = std::invoke_result_t<MatchesProjection, const Arg&>;
35 using describe_reference = std::invoke_result_t<DescribeProjection, const Arg&>;
36
37 Arg arg;
38
39 decltype(auto) as_matches_arg() const noexcept(std::is_nothrow_invocable_v<MatchesProjection, const Arg&>)
40 {
41 return std::invoke(MatchesProjection{}, arg);
42 }
43
44 decltype(auto) as_describe_arg() const noexcept(std::is_nothrow_invocable_v<DescribeProjection, const Arg&>)
45 {
46 return std::invoke(DescribeProjection{}, arg);
47 }
48 };
49
50 template <typename T>
51 struct to_arg_storage
52 {
53 using type = arg_storage<T>;
54 };
55
56 template <typename Arg, typename MatchesProjection, typename DescribeProjection>
57 struct to_arg_storage<arg_storage<Arg, MatchesProjection, DescribeProjection>>
58 {
59 using type = arg_storage<Arg, MatchesProjection, DescribeProjection>;
60 };
61
62 template <typename T>
63 using to_arg_storage_t = typename to_arg_storage<T>::type;
64}
65
67{
74 template <typename Predicate, typename... AdditionalArgs>
75 requires std::is_move_constructible_v<Predicate>
76 && (... && std::is_move_constructible_v<AdditionalArgs>)
78 {
79 private:
80 using storage_t = std::tuple<detail::to_arg_storage_t<AdditionalArgs>...>;
81 template <typename T>
82 using matches_reference_t = typename detail::to_arg_storage_t<T>::matches_reference;
83
84 public:
85 [[nodiscard]]
86 explicit constexpr PredicateMatcher(
87 Predicate predicate,
88 StringViewT fmt,
89 StringViewT invertedFmt,
90 std::tuple<AdditionalArgs...> additionalArgs = {})
91 noexcept(
92 std::is_nothrow_move_constructible_v<Predicate>
93 && (... && std::is_nothrow_move_constructible_v<AdditionalArgs>))
94 : m_Predicate{std::move(predicate)},
95 m_FormatString{std::move(fmt)},
96 m_InvertedFormatString{std::move(invertedFmt)},
97 m_AdditionalArgs{std::move(additionalArgs)}
98 {
99 }
100
101 template <typename First, typename... Others>
102 requires std::predicate<
103 const Predicate&,
104 First&,
105 Others&...,
106 matches_reference_t<AdditionalArgs>...>
107 [[nodiscard]]
108 constexpr bool matches(
109 First& first,
110 Others&... others) const
111 noexcept(
112 std::is_nothrow_invocable_v<
113 const Predicate&,
114 First&,
115 Others&...,
116 matches_reference_t<AdditionalArgs>...>)
117 {
118 return std::apply(
119 [&, this](auto&... additionalArgs) {
120 return std::invoke(
121 m_Predicate,
122 first,
123 others...,
124 additionalArgs.as_matches_arg()...);
125 },
126 m_AdditionalArgs);
127 }
128
129 [[nodiscard]]
130 constexpr StringT describe() const
131 {
132 return std::apply(
133 [&](auto&... additionalArgs) {
134 std::array<StringT, sizeof...(additionalArgs)> const descriptions{additionalArgs.as_describe_arg()...};
135
136 return std::invoke(
137 [&]<std::size_t... indices>(std::index_sequence<indices...> const /*seq*/) {
138 return format::vformat(m_FormatString, format::make_format_args(descriptions[indices]...));
139 },
140 std::index_sequence_for<AdditionalArgs...>{});
141 },
142 m_AdditionalArgs);
143 }
144
145 [[nodiscard]]
146 constexpr auto operator!() const&
147 requires std::is_copy_constructible_v<Predicate>
148 && std::is_copy_constructible_v<storage_t>
149 {
150 return make_inverted(
151 m_Predicate,
152 m_InvertedFormatString,
153 m_FormatString,
154 m_AdditionalArgs);
155 }
156
157 [[nodiscard]]
158 constexpr auto operator!() &&
159 {
160 return make_inverted(
161 std::move(m_Predicate),
162 std::move(m_InvertedFormatString),
163 std::move(m_FormatString),
164 std::move(m_AdditionalArgs));
165 }
166
167 private:
168 [[no_unique_address]] Predicate m_Predicate;
169 StringViewT m_FormatString;
170 StringViewT m_InvertedFormatString;
171 storage_t m_AdditionalArgs;
172
173 template <typename Fn>
174 [[nodiscard]]
175 static constexpr auto make_inverted(
176 Fn&& fn,
177 StringViewT fmt,
178 StringViewT invertedFmt,
179 storage_t tuple)
180 {
181 using NotFnT = decltype(std::not_fn(std::forward<Fn>(fn)));
183 std::not_fn(std::forward<Fn>(fn)),
184 std::move(fmt),
185 std::move(invertedFmt),
186 std::move(tuple)};
187 }
188 };
189
190 namespace detail
191 {
192 class CapturedValue
193 {
194 public:
195 template <typename T>
196 explicit constexpr CapturedValue(std::in_place_t const /*tag*/, T&& value, format::format_string<format::fallback_formattable_t<T>> fmt = "{}")
197 : m_Value{std::forward<T>(value)},
198 m_Fmt{fmt.get()},
199 m_PrintStrategy{&CapturedValue::print_to<std::remove_cvref_t<T>>}
200 {
201 }
202
203 using OutIter = std::ostreambuf_iterator<char>;
204 OutIter print_to(OutIter out) const // NOLINT(*-use-nodiscard)
205 {
206 MIMICPP_ASSERT(m_PrintStrategy, "Null print strategy.");
207 MIMICPP_ASSERT(m_Value.has_value(), "Null capture.");
208
209 return m_PrintStrategy(std::move(out), m_Fmt, m_Value);
210 }
211
212 private:
213 std::any m_Value;
215 using PrintStrategy = OutIter(*)(OutIter, format::vformat_string const&, std::any const&);
216 PrintStrategy m_PrintStrategy;
217
218 template <typename T>
219 static OutIter print_to(OutIter out, format::vformat_string const& fmt, std::any const& capture)
220 {
221 auto formatter = format::fallback_formattable(std::any_cast<std::remove_cvref_t<T>>(capture));
222 return format::vformat_to(std::move(out), fmt, format::make_format_args(formatter));
223 }
224 };
225 }
226
227 template <util::unqualified... Args>
229 {
230 using CapturedValue = detail::CapturedValue;
231 using CaptureSink = std::back_insert_iterator<std::vector<CapturedValue>>;
232
233 public:
234 using expectation_refs = std::tuple<std::unwrap_reference_t<Args> const&...>;
235
236 [[nodiscard]]
237 explicit constexpr MatchEvaluationContext(expectation_refs expectations, CaptureSink captureSink)
238 : m_Expectations{std::move(expectations)},
239 m_captureSink{std::move(captureSink)}
240 {
241 }
242
243 [[nodiscard]]
244 constexpr expectation_refs expectations() const noexcept
245 {
246 return m_Expectations;
247 }
248
249 template <typename T>
250 void capture(T&& value)
251 {
252 *m_captureSink++ = CapturedValue{std::in_place, std::forward<T>(value)};
253 }
254
255 template <typename T>
256 void capture(T&& value, format::format_string<format::fallback_formattable_t<T>> fmt)
257 {
258 *m_captureSink++ = CapturedValue{std::in_place, std::forward<T>(value), std::move(fmt)};
259 }
260
261 private:
262 expectation_refs m_Expectations;
263 CaptureSink m_captureSink;
264 };
265
266 template <bool isInverted, util::unqualified Predicate, util::unqualified... Args>
267 requires std::is_move_constructible_v<Predicate>
268 && (... && std::is_move_constructible_v<Args>)
270 {
271 public:
273 using ArgsStorage = std::tuple<Args...>;
274
275 [[nodiscard]]
276 explicit constexpr GenericMatcher(Predicate predicate, format::vformat_string fmt, ArgsStorage args)
277 noexcept(
278 std::is_nothrow_move_constructible_v<Predicate>
279 && std::is_nothrow_move_constructible_v<ArgsStorage>)
280 : m_Predicate{std::move(predicate)},
281 m_FormatString{std::move(fmt)},
282 m_Args{std::move(args)}
283 {
284 }
285
286 template <typename First, typename... Others>
287 requires std::predicate<Predicate const&, Context&, First&, Others&...>
288 [[nodiscard]]
289 expectation::MatchResult matches(First& first, Others&... others) const
290 {
291 std::vector<detail::CapturedValue> captures{};
292
293 StringStreamT ss{};
294 describe_to(std::ostreambuf_iterator{ss});
295
296 if (Context ctx{m_Args, std::back_inserter(captures)};
297 !isInverted == std::invoke(m_Predicate, ctx, first, others...))
298 {
299 return expectation::MatchSuccess{.description = std::move(ss).str()};
300 }
301
302 if (!std::ranges::empty(captures))
303 {
304 ss << ", but actually ";
305 auto iter = captures.cbegin();
306 iter->print_to(std::ostreambuf_iterator{ss});
307
308 for (++iter; iter != captures.cend(); ++iter)
309 {
310 ss << ", ";
311 iter->print_to(std::ostreambuf_iterator{ss});
312 }
313 }
314
315 return expectation::MatchFailure{.description = std::move(ss).str()};
316 }
317
318 [[nodiscard]]
319 constexpr auto operator!() const&
320 requires std::is_copy_constructible_v<Predicate>
321 && std::is_copy_constructible_v<ArgsStorage>
322 {
323 return GenericMatcher<!isInverted, Predicate, Args...>{m_Predicate, m_FormatString, m_Args};
324 }
325
326 [[nodiscard]]
327 constexpr auto operator!() &&
328 {
329 return GenericMatcher<!isInverted, Predicate, Args...>{std::move(m_Predicate), std::move(m_FormatString), std::move(m_Args)};
330 }
331
332 private:
333 Predicate m_Predicate;
334 format::vformat_string m_FormatString;
335 ArgsStorage m_Args;
336
337 template <print_iterator OutIter>
338 OutIter describe_to(OutIter out) const
339 {
340 if constexpr (isInverted)
341 {
342 out = format::format_to(std::move(out), "not (");
343 }
344
345 out = std::apply(
346 [&](auto&&... args) {
347 return format::vformat_to(std::move(out), m_FormatString, format::make_format_args(args...));
348 },
349 std::tuple<format::fallback_formattable_t<Args const&>...>{m_Args});
350
351 if constexpr (isInverted)
352 {
353 out = format::format_to(std::move(out), ")");
354 }
355
356 return out;
357 }
358 };
359
360 template <typename Predicate, typename... Args>
361 [[nodiscard]]
362 constexpr auto make_generic_matcher(
363 Predicate && predicate,
364 format::format_string<format::fallback_formattable_t<Args>...> const formatString,
365 Args && ... args)
366 {
367 using Matcher = GenericMatcher<false, std::remove_cvref_t<Predicate>, std::remove_cvref_t<Args>...>;
368 return Matcher{
369 std::forward<Predicate>(predicate),
370 formatString.get(),
371 std::forward_as_tuple(std::forward<Args>(args)...)};
372 }
373
380 {
381 public:
382 [[nodiscard]]
383 static expectation::MatchResult matches(auto&& /*target*/) noexcept
384 {
386 }
387 };
388
394 template <typename T>
396 {
397 public:
398 template <typename U>
399 using is_accepting = std::is_same<T, U>;
400
401 [[nodiscard]]
402 static expectation::MatchResult matches(auto&& /*target*/) noexcept
403 {
405 }
406 };
407}
408
410{
467
472 [[maybe_unused]] inline constexpr WildcardMatcher _{};
473
479 template <typename T>
480 [[maybe_unused]] inline constexpr TypeMatcher<T> type{};
481
487 template <typename T>
488 [[nodiscard]]
489 constexpr auto eq(T&& value)
490 {
491 return PredicateMatcher{
492 std::equal_to{},
493 "== {}",
494 "!= {}",
495 std::make_tuple(std::forward<T>(value))};
496 }
497
503 template <typename T>
504 [[nodiscard]]
505 constexpr auto ne(T&& value)
506 {
507 return PredicateMatcher{
508 std::not_equal_to{},
509 "!= {}",
510 "== {}",
511 std::make_tuple(std::forward<T>(value))};
512 }
513
519 template <typename T>
520 [[nodiscard]]
521 constexpr auto lt(T&& value)
522 {
523 return PredicateMatcher{
524 std::less{},
525 "< {}",
526 ">= {}",
527 std::make_tuple(std::forward<T>(value))};
528 }
529
535 template <typename T>
536 [[nodiscard]]
537 constexpr auto le(T&& value)
538 {
539 return PredicateMatcher{
540 std::less_equal{},
541 "<= {}",
542 "> {}",
543 std::make_tuple(std::forward<T>(value))};
544 }
545
551 template <typename T>
552 [[nodiscard]]
553 constexpr auto gt(T&& value)
554 {
555 return PredicateMatcher{
556 std::greater{},
557 "> {}",
558 "<= {}",
559 std::make_tuple(std::forward<T>(value))};
560 }
561
567 template <typename T>
568 [[nodiscard]]
569 constexpr auto ge(T&& value)
570 {
571 return PredicateMatcher{
572 std::greater_equal{},
573 ">= {}",
574 "< {}",
575 std::make_tuple(std::forward<T>(value))};
576 }
577
586 template <typename UnaryPredicate>
587 [[nodiscard]]
588 constexpr auto predicate(
589 UnaryPredicate&& predicate,
590 StringViewT description = "passes predicate",
591 StringViewT invertedDescription = "fails predicate")
592 {
593 return PredicateMatcher{
594 std::forward<UnaryPredicate>(predicate),
595 std::move(description),
596 std::move(invertedDescription),
597 };
598 }
599
606 template <util::satisfies<std::is_lvalue_reference> T>
607 [[nodiscard]]
608 constexpr auto instance(T&& instance) // NOLINT(cppcoreguidelines-missing-std-forward)
609 {
611 []<typename Other>(auto& ctx, Other const& target) noexcept
612 requires std::is_convertible_v<std::remove_reference_t<T> const volatile*, Other const volatile*>
613 {
614 auto const& [expected] = ctx.expectations();
615 void const* const targetPtr = std::addressof(target);
616 ctx.capture(targetPtr);
617 return expected == targetPtr;
618 },
619 "is instance at {}",
620 static_cast<void const*>(std::addressof(instance)));
621 }
622
626}
627
628#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:54
#define MIMICPP_DETAIL_MODULE_EXPORT
Definition Config.hpp:19
Definition GeneralMatchers.hpp:270
constexpr GenericMatcher(Predicate predicate, format::vformat_string fmt, ArgsStorage args) noexcept(std::is_nothrow_move_constructible_v< Predicate > &&std::is_nothrow_move_constructible_v< ArgsStorage >)
Definition GeneralMatchers.hpp:276
constexpr auto operator!() &&
Definition GeneralMatchers.hpp:327
MatchEvaluationContext< Args... > Context
Definition GeneralMatchers.hpp:272
constexpr auto operator!() const &
Definition GeneralMatchers.hpp:319
expectation::MatchResult matches(First &first, Others &... others) const
Definition GeneralMatchers.hpp:289
std::tuple< Args... > ArgsStorage
Definition GeneralMatchers.hpp:273
Definition GeneralMatchers.hpp:229
constexpr MatchEvaluationContext(expectation_refs expectations, CaptureSink captureSink)
Definition GeneralMatchers.hpp:237
void capture(T &&value)
Definition GeneralMatchers.hpp:250
std::tuple< std::unwrap_reference_t< Args > const &... > expectation_refs
Definition GeneralMatchers.hpp:234
constexpr expectation_refs expectations() const noexcept
Definition GeneralMatchers.hpp:244
void capture(T &&value, format::format_string< format::fallback_formattable_t< T > > fmt)
Definition GeneralMatchers.hpp:256
Generic matcher and the basic building block of most of the built-in matchers.
Definition GeneralMatchers.hpp:78
constexpr bool matches(First &first, Others &... others) const noexcept(std::is_nothrow_invocable_v< const Predicate &, First &, Others &..., matches_reference_t< AdditionalArgs >... >)
Definition GeneralMatchers.hpp:108
constexpr auto operator!() const &
Definition GeneralMatchers.hpp:146
constexpr auto operator!() &&
Definition GeneralMatchers.hpp:158
constexpr StringT describe() const
Definition GeneralMatchers.hpp:130
constexpr PredicateMatcher(Predicate predicate, StringViewT fmt, StringViewT invertedFmt, std::tuple< AdditionalArgs... > additionalArgs={}) noexcept(std::is_nothrow_move_constructible_v< Predicate > &&(... &&std::is_nothrow_move_constructible_v< AdditionalArgs >))
Definition GeneralMatchers.hpp:86
Matcher, which can be used to disambiguate between similar overloads.
Definition GeneralMatchers.hpp:396
static expectation::MatchResult matches(auto &&) noexcept
Definition GeneralMatchers.hpp:402
std::is_same< T, U > is_accepting
Definition GeneralMatchers.hpp:399
Matcher, which never fails.
Definition GeneralMatchers.hpp:380
static expectation::MatchResult matches(auto &&) noexcept
Definition GeneralMatchers.hpp:383
Determines, whether T is unqualified.
Definition Concepts.hpp:90
constexpr auto lt(T &&value)
Tests, whether the target is less than the expected value.
Definition GeneralMatchers.hpp:521
constexpr auto instance(T &&instance)
Tests whether the target is the expected instance.
Definition GeneralMatchers.hpp:608
constexpr auto le(T &&value)
Tests, whether the target is less than or equal to the expected value.
Definition GeneralMatchers.hpp:537
constexpr TypeMatcher< T > type
Matcher, which can be used as a last resort to disambiguate similar overloads.
Definition GeneralMatchers.hpp:480
constexpr WildcardMatcher _
The wildcard matcher, always matching.
Definition GeneralMatchers.hpp:472
constexpr auto eq(T &&value)
Tests, whether the target compares equal to the expected value.
Definition GeneralMatchers.hpp:489
constexpr auto gt(T &&value)
Tests, whether the target is greater than the expected value.
Definition GeneralMatchers.hpp:553
constexpr auto predicate(UnaryPredicate &&predicate, StringViewT description="passes predicate", StringViewT invertedDescription="fails predicate")
Tests, whether the target fulfills the given predicate.
Definition GeneralMatchers.hpp:588
constexpr auto ge(T &&value)
Tests, whether the target is greater than or equal to the expected value.
Definition GeneralMatchers.hpp:569
constexpr auto ne(T &&value)
Tests, whether the target compares not equal to the expected value.
Definition GeneralMatchers.hpp:505
std::variant< MatchSuccess, MatchFailure > MatchResult
Definition Common.hpp:41
constexpr decltype(auto) fallback_formattable(T &&target)
Definition Format.hpp:225
StringViewT vformat_string
Definition Format.hpp:89
OutIter format_to(OutIter out, format_string< Args... > const fmt, Args &&... args)
Definition Format.hpp:175
std::basic_format_string< CharT, std::type_identity_t< Args >... > format_string
Definition Format.hpp:88
decltype(fallback_formattable(std::declval< T >())) fallback_formattable_t
Definition Format.hpp:238
Definition FloatingPointMatchers.hpp:25
Definition Call.hpp:24
constexpr auto make_generic_matcher(Predicate &&predicate, format::format_string< format::fallback_formattable_t< Args >... > const formatString, Args &&... args)
Definition GeneralMatchers.hpp:362
std::basic_ostringstream< CharT, CharTraitsT > StringStreamT
Definition Format.hpp:38
std::basic_string_view< CharT, CharTraitsT > StringViewT
Definition Fwd.hpp:398
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:397
Definition Common.hpp:35
Definition Common.hpp:27