summaryrefslogtreecommitdiff
path: root/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h')
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h
new file mode 100644
index 000000000..328627512
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h
@@ -0,0 +1,97 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+
+#include <catch/catch.hpp>
+
+#include <optional>
+#include <cassert>
+
+namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE {
+
+ template<typename T>
+ class GeneratorHandler : public Catch::Generators::IGenerator<T> {
+ public:
+
+ GeneratorHandler(Catch::Generators::GeneratorWrapper<T>&& generator)
+ : generator{std::move(generator)},
+ first_call{true}
+ {}
+
+ T const& get() const override {
+ assert(!first_call);
+ return generator.get();
+ }
+
+ bool next() override {
+ if (first_call) {
+ first_call = false;
+ return true;
+ }
+
+ return generator.next();
+ }
+
+ private:
+ Catch::Generators::GeneratorWrapper<T> generator;
+ bool first_call;
+ };
+
+
+ /*!
+ * Returns a generator wrapping \a generator that ensures that
+ * changes its semantics so that the first call to get should be
+ * preceded by a call to next.
+ *
+ * Catch generators require that is valid to call get and obtain a
+ * valid value on a generator that was just created.
+ * That is, generators should be non-empty and their first value
+ * should be initialized on construction.
+ *
+ * Normally, this is not a problem, and the next implementation of
+ * the generator can be simply called in the constructor.
+ * But when a generator depends on other generators, doing so will
+ * generally skip the first value that the generator
+ * produces, as the wrapping generator will need to advance the
+ * underlying generator, losing the value in the process.
+ * This is in particular, a problem, on generators that are finite
+ * or infinite and ordered.
+ *
+ * To solve the issue, the original value can be saved before
+ * advancing the generator or some code can be duplicated or
+ * abstracted so that what a new element can be generated without
+ * advancing the underlying generator.
+ *
+ * While this is acceptable, it can be error prone on more complex
+ * generators, generators that randomly access a collection of
+ * generators and so on.
+ *
+ * To simplify this process, this generator changes the semantics
+ * of the wrapped generator such that the first value of the
+ * generator is produced after the first call to next and the
+ * generator is considered in an invalid state before the first
+ * advancement.
+ *
+ * In this way, by wrapping all generators that a generator
+ * depends on, the implementation required for the first value is
+ * the same as the one required for all following values, with
+ * regards to the sequencing of next and get operations,
+ * simplifying the implementation of dependent generators.
+ *
+ * Do note that, while the generator returned by this function
+ * implments the generator interface that Catch2 requires, it
+ * cannot be normally used as a generator as it fails to comply
+ * with the first value semantics that a generator requires.
+ * Indeed, it should only be used as an intermediate wrapper for
+ * the implementation of generators that depends on other
+ * generators.
+ */
+ template<typename T>
+ inline Catch::Generators::GeneratorWrapper<T> handler(Catch::Generators::GeneratorWrapper<T>&& generator) {
+ return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new GeneratorHandler(std::move(generator))));
+ }
+
+} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE