c++catch2

C++ Catch2 : Generate a array with random values


I try to implement a generator in catch2 of an std::array of 1024 random values. Below my code

#include "catch2/catch_all.hpp"
#include <array>
#include <vector>

    TEST_CASE("Foo") {
        auto s = GENERATE(as<std::array<char, 1024> >{}, 'a');
        REQUIRE(!s.empty());
    }

The following error is generated

 In file included from /opt/compiler-explorer/libs/catch2/v3.0.0-preview3/src/catch2/generators/catch_generators_all.hpp:25,
                 from /opt/compiler-explorer/libs/catch2/v3.0.0-preview3/src/catch2/catch_all.hpp:46,
                 from <source>:1:
/opt/compiler-explorer/libs/catch2/v3.0.0-preview3/src/catch2/generators/catch_generators.hpp: In instantiation of 'Catch::Generators::Generators<T> Catch::Generators::makeGenerators(as<T>, U&&, Gs&& ...) [with T = std::array<char, 1024>; U = char; Gs = {}]':
<source>:7:14:   required from here
/opt/compiler-explorer/libs/catch2/v3.0.0-preview3/src/catch2/generators/catch_generators.hpp:181:39: error: array must be initialized with a brace-enclosed initializer
  181 |         return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
      |   

Any idea how to overcome this issue?

Example


Solution

  • Documentation how to provide custom generators in Catch2 sucks. Had to review Catch2 code to figure out how other generators are implemented and got this:

    template <typename T, typename Dist>
    struct FillRandomGenerator : Catch::Generators::IGenerator<T> {
        template <typename D>
        explicit FillRandomGenerator(size_t repeatCount, D&& dist)
            : repeatCount { repeatCount }
            , distr { std::forward<D>(dist) }
        {
            fillRandom();
        }
    
        T value;
        size_t repeatCount;
        Dist distr;
        std::default_random_engine randGen {};
    
        bool next() override
        {
            fillRandom();
            return --repeatCount;
        }
    
        T const& get() const override
        {
            return value;
        }
    
        void fillRandom()
        {
            std::generate(std::begin(value), std::end(value), [&]() { return distr(randGen); });
        }
    };
    
    template <typename T, typename Dist>
    Catch::Generators::GeneratorWrapper<T> fillRandom(size_t repeatCount, Dist&& distr)
    {
        return Catch::Generators::GeneratorWrapper<T>(Catch::Detail::make_unique<FillRandomGenerator<T, std::decay_t<Dist>>>(repeatCount, std::forward<Dist>(distr)));
    }
    

    https://godbolt.org/z/jfzT15Tcv

    There is lots of room for improvement, but general idea should be clear. With current implementation works only for std::array, other containers will remain empty.

    I do not like that I had to use Catch::Detail::make_unique. Detail usually means user of library should not touch symbols in this namespace since. symbols there can change without any warning in next release.