diff options
Diffstat (limited to 'src/qdoc/catch_generators/src/catch_generators/utilities/semantics')
3 files changed, 185 insertions, 0 deletions
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h new file mode 100644 index 000000000..57798be1a --- /dev/null +++ b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h @@ -0,0 +1,26 @@ +// 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 <type_traits> + +namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { + + /*! + * Forces \value to be copied in an expression context. + * + * This is used in contexts where inferences of a type that + * requires generality might identify a reference when ownership + * is required. + * + * Note that the compiler might optmize the copy away. This is a + * non-issue as we are only interested in breaking lifetime + * dependencies. + */ + template<typename T> + std::remove_reference_t<T> copy_value(T value) { return value; } + +} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE 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 diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h new file mode 100644 index 000000000..5e780085b --- /dev/null +++ b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h @@ -0,0 +1,62 @@ +// Copyright (C) 2021 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 <vector> +#include <tuple> + +namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { + + namespace QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE { + + /*! + * Returns the type of the first element of Args. + * + * Args is expected to have at least one + */ + template<typename... Args> + using first_from_pack_t = std::tuple_element_t<0, std::tuple<Args...>>; + + } // end QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE + + + /*! + * Builds an std::vector by moving \a movables into it. + * + * \a movables must be made of homogenous types. + * + * This function is intended to allow the construction of an + * std::vector<T>, where T is a move only type, as an expression, + * to lighten the idiom. + * + * For example, Catch's GeneratorWrapper<T> adapts a + * std::unique_ptr, which is move only, making it impossible to + * build a std::vector from them in place. + * + * Then, everywhere this is needed, a more complex approach of + * generating the collection of objects, generating a vector of a + * suitable size and iterating the objects to move-emplace them in + * the vector is required. + * + * This not only complicates the code but is incompatible with a + * GENERATE expression, making it extremely hard, noisy and error + * prone to use them together. + * + * In those cases, then, a call to move_into_vector can be used as + * an expression to circumvent the problem. + */ + template<typename... MoveOnlyTypes> + inline auto move_into_vector(MoveOnlyTypes... movables) { + std::vector<QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE::first_from_pack_t<MoveOnlyTypes...>> + moved_into_vector; + moved_into_vector.reserve(sizeof...(movables)); + + (moved_into_vector.emplace_back(std::move(movables)), ...); + + return moved_into_vector; + } + +} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE |