Skip to content

Commit 9eca713

Browse files
committed
Add option to skip forward to the generator interface
1 parent 44c597f commit 9eca713

22 files changed

Lines changed: 438 additions & 18 deletions

docs/generators.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,23 @@ struct IGenerator : GeneratorUntypedBase {
264264
// Returns user-friendly string showing the current generator element
265265
// Does not have to be overridden, IGenerator provides default implementation
266266
virtual std::string stringifyImpl() const;
267+
268+
/**
269+
* Customization point for `skipToNthElement`
270+
*
271+
* Does not have to be overridden, there is a default implementation.
272+
* Can be overridden for better performance.
273+
*
274+
* If there are not enough elements, shall throw an error.
275+
*
276+
* Going backwards is not supported.
277+
*/
278+
virtual void skipToNthElementImpl( std::size_t n );
267279
};
268280
```
269281
282+
> `skipToNthElementImpl` was added in Catch2 vX.Y.Z
283+
270284
However, to be able to use your custom generator inside `GENERATE`, it
271285
will need to be wrapped inside a `GeneratorWrapper<T>`.
272286
`GeneratorWrapper<T>` is a value wrapper around a

examples/300-Gen-OwnGenerator.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ class RandomIntGenerator final : public Catch::Generators::IGenerator<int> {
3939
current_number = m_dist(m_rand);
4040
return true;
4141
}
42+
43+
// Note: this improves the performance only a bit, but it is here
44+
// to show how you can override the skip functionality.
45+
void skipToNthElementImpl( std::size_t n ) override {
46+
auto current_index = currentElementIndex();
47+
assert(current_index <= n);
48+
// We cannot jump forward the underlying generator directly,
49+
// because we do not know how many bits each distributed number
50+
// would consume to be generated.
51+
for (; current_index < n; ++current_index) {
52+
(void)m_dist(m_rand);
53+
}
54+
55+
// We do not have to touch the current element index; it is handled
56+
// by the base class.
57+
}
4258
};
4359

4460
// Avoids -Wweak-vtables

src/catch2/interfaces/catch_interfaces_generatortracker.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// SPDX-License-Identifier: BSL-1.0
88

99
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
10+
#include <catch2/generators/catch_generators.hpp>
11+
1012
#include <string>
1113

1214
namespace Catch {
@@ -21,6 +23,31 @@ namespace Catch {
2123
return ret;
2224
}
2325

26+
void GeneratorUntypedBase::skipToNthElementImpl( std::size_t n ) {
27+
for ( size_t i = m_currentElementIndex; i < n; ++i ) {
28+
bool isValid = next();
29+
if ( !isValid ) {
30+
Detail::throw_generator_exception(
31+
"Coud not jump to Nth element: not enough elements" );
32+
}
33+
}
34+
}
35+
36+
void GeneratorUntypedBase::skipToNthElement( std::size_t n ) {
37+
if ( n < m_currentElementIndex ) {
38+
Detail::throw_generator_exception(
39+
"Tried to jump generator backwards" );
40+
}
41+
if ( n == m_currentElementIndex ) { return; }
42+
43+
skipToNthElementImpl(n);
44+
// Fixup tracking after moving the generator forward
45+
// * Ensure that the correct element index is set after skipping
46+
// * Invalidate cache
47+
m_currentElementIndex = n;
48+
m_stringReprCache.clear();
49+
}
50+
2451
StringRef GeneratorUntypedBase::currentElementAsString() const {
2552
if ( m_stringReprCache.empty() ) {
2653
m_stringReprCache = stringifyImpl();

src/catch2/interfaces/catch_interfaces_generatortracker.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ namespace Catch {
3535
//! Customization point for `currentElementAsString`
3636
virtual std::string stringifyImpl() const = 0;
3737

38+
/**
39+
* Customization point for skipping to the n-th element
40+
*
41+
* Defaults to successively calling `countedNext`. If there
42+
* are not enough elements to reach the nth one, will throw
43+
* an error.
44+
*/
45+
virtual void skipToNthElementImpl( std::size_t n );
46+
3847
public:
3948
GeneratorUntypedBase() = default;
4049
// Generation of copy ops is deprecated (and Clang will complain)
@@ -58,6 +67,13 @@ namespace Catch {
5867

5968
std::size_t currentElementIndex() const { return m_currentElementIndex; }
6069

70+
/**
71+
* Moves the generator forward **to** the n-th element
72+
*
73+
* Cannot move backwards. Can stay in place.
74+
*/
75+
void skipToNthElement( std::size_t n );
76+
6177
/**
6278
* Returns generator's current element as user-friendly string.
6379
*

tests/SelfTest/Baselines/automake.sw.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ Nor would this
170170
:test-result: PASS GENERATE can combine literals and generators
171171
:test-result: PASS Generators -- adapters
172172
:test-result: PASS Generators -- simple
173+
:test-result: PASS Generators can be skipped forward
173174
:test-result: PASS Generators internals
174175
:test-result: PASS Greater-than inequalities with different epsilons
175176
:test-result: PASS Hashers with different seed produce different hash with same test case

tests/SelfTest/Baselines/automake.sw.multi.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@
168168
:test-result: PASS GENERATE can combine literals and generators
169169
:test-result: PASS Generators -- adapters
170170
:test-result: PASS Generators -- simple
171+
:test-result: PASS Generators can be skipped forward
171172
:test-result: PASS Generators internals
172173
:test-result: PASS Greater-than inequalities with different epsilons
173174
:test-result: PASS Hashers with different seed produce different hash with same test case

tests/SelfTest/Baselines/compact.sw.approved.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,15 @@ Generators.tests.cpp:<line number>: passed: j < i for: -1 < 3
801801
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 1
802802
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 2
803803
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 3
804+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 0 for: 0 == 0
805+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 3 for: 3 == 3
806+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 3 for: 3 == 3
807+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 3 for: 3 == 3
808+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 3 for: 3 == 3
809+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 5 for: 5 == 5
810+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 5 for: 5 == 5
811+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 3 )
812+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 6 )
804813
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 123 for: 123 == 123
805814
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
806815
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
@@ -2915,7 +2924,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
29152924
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
29162925
Misc.tests.cpp:<line number>: passed:
29172926
Misc.tests.cpp:<line number>: passed:
2918-
test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected
2919-
assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected
2927+
test cases: 440 | 320 passed | 96 failed | 6 skipped | 18 failed as expected
2928+
assertions: 2340 | 2139 passed | 158 failed | 43 failed as expected
29202929

29212930

tests/SelfTest/Baselines/compact.sw.multi.approved.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,15 @@ Generators.tests.cpp:<line number>: passed: j < i for: -1 < 3
799799
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 1
800800
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 2
801801
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 3
802+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 0 for: 0 == 0
803+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 3 for: 3 == 3
804+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 3 for: 3 == 3
805+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 3 for: 3 == 3
806+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 3 for: 3 == 3
807+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.currentElementIndex() == 5 for: 5 == 5
808+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.get() == 5 for: 5 == 5
809+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 3 )
810+
GeneratorsImpl.tests.cpp:<line number>: passed: generator.skipToNthElement( 6 )
802811
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 123 for: 123 == 123
803812
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
804813
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
@@ -2904,7 +2913,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
29042913
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
29052914
Misc.tests.cpp:<line number>: passed:
29062915
Misc.tests.cpp:<line number>: passed:
2907-
test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected
2908-
assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected
2916+
test cases: 440 | 320 passed | 96 failed | 6 skipped | 18 failed as expected
2917+
assertions: 2340 | 2139 passed | 158 failed | 43 failed as expected
29092918

29102919

tests/SelfTest/Baselines/console.std.approved.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,6 @@ due to unexpected exception with message:
17431743
Why would you throw a std::string?
17441744

17451745
===============================================================================
1746-
test cases: 439 | 337 passed | 76 failed | 7 skipped | 19 failed as expected
1747-
assertions: 2309 | 2130 passed | 136 failed | 43 failed as expected
1746+
test cases: 440 | 338 passed | 76 failed | 7 skipped | 19 failed as expected
1747+
assertions: 2318 | 2139 passed | 136 failed | 43 failed as expected
17481748

tests/SelfTest/Baselines/console.sw.approved.txt

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5921,6 +5921,53 @@ Generators.tests.cpp:<line number>: PASSED:
59215921
with expansion:
59225922
12 > 3
59235923

5924+
-------------------------------------------------------------------------------
5925+
Generators can be skipped forward
5926+
-------------------------------------------------------------------------------
5927+
GeneratorsImpl.tests.cpp:<line number>
5928+
...............................................................................
5929+
5930+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5931+
REQUIRE( generator.currentElementIndex() == 0 )
5932+
with expansion:
5933+
0 == 0
5934+
5935+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5936+
REQUIRE( generator.currentElementIndex() == 3 )
5937+
with expansion:
5938+
3 == 3
5939+
5940+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5941+
REQUIRE( generator.get() == 3 )
5942+
with expansion:
5943+
3 == 3
5944+
5945+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5946+
REQUIRE( generator.currentElementIndex() == 3 )
5947+
with expansion:
5948+
3 == 3
5949+
5950+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5951+
REQUIRE( generator.get() == 3 )
5952+
with expansion:
5953+
3 == 3
5954+
5955+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5956+
REQUIRE( generator.currentElementIndex() == 5 )
5957+
with expansion:
5958+
5 == 5
5959+
5960+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5961+
REQUIRE( generator.get() == 5 )
5962+
with expansion:
5963+
5 == 5
5964+
5965+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5966+
REQUIRE_THROWS( generator.skipToNthElement( 3 ) )
5967+
5968+
GeneratorsImpl.tests.cpp:<line number>: PASSED:
5969+
REQUIRE_THROWS( generator.skipToNthElement( 6 ) )
5970+
59245971
-------------------------------------------------------------------------------
59255972
Generators internals
59265973
Single value
@@ -19542,6 +19589,6 @@ Misc.tests.cpp:<line number>
1954219589
Misc.tests.cpp:<line number>: PASSED:
1954319590

1954419591
===============================================================================
19545-
test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected
19546-
assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected
19592+
test cases: 440 | 320 passed | 96 failed | 6 skipped | 18 failed as expected
19593+
assertions: 2340 | 2139 passed | 158 failed | 43 failed as expected
1954719594

0 commit comments

Comments
 (0)