Skip to content

Conversation

@theo-dep
Copy link

@theo-dep theo-dep commented Dec 5, 2025

Description

Currently, Catch2's GENERATE macro supports individual values, ranges, and containers, but lacks native support for tuple-like generation in a single call.
This PR introduces an utility (tuple_as_range) to seamlessly generate tuples of heterogeneous types, enabling cleaner test cases with mixed-type inputs.

It is more or less like TEMPLATE_TEST_CASE_SIG but with the powerful of Catch2's generators.

Usage will be:

TEST_CASE("Heterogeneous Generation", "[generators]") {
    auto accessor = GENERATE(tuple_as_range(42, "foo", 3.14));
    accessor.perform([](const auto& element) {
        // element is sequentially: 42, "foo", 3.14
    });
}

I will complete the PR with documentation, example and _all headers if you think it is a good add.

Both blogs inspired me:
https://www.fluentcpp.com/2019/03/08/stl-algorithms-on-tuples/
https://www.foonathan.net/2017/03/tuple-iterator/

GitHub Issues

None.

@horenmar
Copy link
Member

horenmar commented Jan 7, 2026

I finally had some time to play with this. Overall I like it, but some preliminary notes:

  • Current implementation is not C++14 compatible.
  • This compiles, but does not work as expected:
    auto test = std::tuple<int, double, float>( 1, 2.0, 3.0f );
    const auto accessor3 = GENERATE_REF( tuple_as_range( test ) );
    SUCCEED();

It runs only once, because the whole tuple is returned as an element, rather than being separated into 3 elements.

  • This does not compile:
    const auto& accessor4 =
        GENERATE( tuple_as_range( 1, 2, 3 ), tuple_as_range( 1.0, 2.0, 3.0 ) );

I don't think there is actually a way to make this compile though and it should be called out in the docs.

If the underlying tuple types are the same, it compiles though, similarly to how other generators can be used:

const int value = GENERATE(1, 2, values({1, 2, 3})); // fine, common type is int
const auto& accessor = GENERATE(tuple_as_range(1, 2, 3), tuple_as_range(4, 5, 6)); // fine, the accessor for both is typed for `std::tuple<int, int, int>`
  • I wonder if it would be possible to make this work (it would probably need a different overload name, to support the case where we want tuple of structs, versus the struct-as-a-tuple):
struct tuplish {
    int i = 1;
    double d = 2.;
    float f = 3.f;
    char c = '4';
};

const auto& accessor = GENERATE(tuple_as_range( tuplish{} ));

I recall that this is barely possible in C++14, but don't remember how bad the compile times/compiler support gets.

@theo-dep
Copy link
Author

theo-dep commented Jan 7, 2026

Thanks you for reviewing this bizarre code :D!

Current implementation is not C++14 compatible.

Apologies, I naively assumed SelfTest would reject me and I am working too much with newer standards.

I also fixed the use of tuple likes as parameter. This is a use case I hadn't considered.

For multiple tuple_as_range in GENERATE, I don't think it is possible. TupleAccessor would need to be an erasure-type to fit into std::vector<GeneratorWrapper<T>> :/. I will think about how to use std::tuple_cat to maybe have only one GeneratorWrapper from multiple tuples.

For your last point, a struct_as_range could be possible with something like visit_struct. What were you thinking about?

@horenmar
Copy link
Member

horenmar commented Jan 8, 2026

Apologies, I naively assumed SelfTest would reject me and I am working too much with newer standards.

It did, on some platforms -> I found the issue when trying to build it locally using MSVC.

For your last point, a struct_as_range could be possible with something like visit_struct. What were you thinking about?

I was looking at magic_get actually, but the basic idea is the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants