forked from Kitware/VTK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vtkPermuteOptions.h
313 lines (273 loc) · 9.81 KB
/
vtkPermuteOptions.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPermuteOptions.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#ifndef vtkPermuteOptions_h
#define vtkPermuteOptions_h
#include <vtkTimeStamp.h>
#include <cassert>
#include <functional>
#include <sstream>
#include <vector>
/**
* vtkPermuteOptions is a class template designed to exhaustively explore the
* parameter space of a vtkObject subclass.
*
* This testing utility can be taught to update parameters that are defined
* using an API similar to the vtkSetGet macros. Concretely, consider testing
* vtkXMLWriter. This class has a number of independent settings: byte order,
* compressor, data mode, and more. When testing this class, it would be ideal
* to test every combination of these parameters, but this would normally
* require a lot of verbose, redundant, error-prone boilerplate code.
*
* This class simplifies this process. The following describes how to use
* vtkPermuteOptions to run a test using all combinations of vtkXMLWriter's
* byte order and compressor settings (just sticking to two options for
* simplicity -- the class has no limit on number of options or number of
* values for those options).
*
* First, the vtkPermuteOptions object must be instantiated, using the
* configured class as the template parameter:
*
* @code
* vtkPermuteOptions<vtkXMLWriter> config;
* @endcode
*
* Next the options and their possible values are specified. Each call to
* AddOptionValue adds a value to a specific option. Options are created
* automatically as new option names are passed to AddOptionValue. The
* following instructs vtkPermuteOptions to test option ByteOrder (with values
* LittleEndian and BigEndian) and CompressorType (with values NONE, ZLIB, and
* LZ4):
*
* @code
* this->AddOptionValue("ByteOrder", &vtkXMLWriter::SetByteOrder,
* "BigEndian", vtkXMLWriter::BigEndian);
* this->AddOptionValue("ByteOrder", &vtkXMLWriter::SetByteOrder,
* "LittleEndian", vtkXMLWriter::LittleEndian);
*
* this->AddOptionValues("CompressorType", &vtkXMLWriter::SetCompressorType,
* "NONE", vtkXMLWriter::NONE,
* "ZLIB", vtkXMLWriter::ZLIB,
* "LZ4", vtkXMLWriter::LZ4);
* @endcode
*
* Note that that there are two variations on how values may be added to an
* option. For ByteOrder, we use AddOptionValue to specify a human-readable
* string that uniquely identifies the option, a member function pointer to the
* option's setter, a human readable string that uniquely identifies the value,
* and the value itself (in this case, an enum value). The first call creates
* the option named "ByteOrder" and adds the "BigEndian" value. The second call
* adds the "LittleEndian" value to the same option.
*
* The CompressorType call uses the variatic function template AddOptionValues
* to specify multiple values to the same option at once. The value-name and
* value pairs are repeated, and each is added to the option with the supplied
* name. Any number of values may be added to a single option this way.
*
* To run through the permutations, a vtk-esque iterator API is used:
*
* @code
* config.InitPermutations();
* while (!config.IsDoneWithPermutations())
* {
* // Testing code...
*
* // Apply the current option permutation to a vtkXMLWriter object:
* vtkXMLWriter *writer = ...;
* config.ApplyCurrentPermutation(writer);
*
* // More testing code...
*
* config.GoToNextPermutation();
* }
* @endcode
*
* This will repeat the testing code, but configure the vtkXMLWriter object
* differently each time. It will perform a total of 6 iterations, with
* parameters:
*
* @code
* Test Iteration ByteOrder CompressorType
* -------------- --------- --------------
* 1 BigEndian NONE
* 2 BigEndian ZLIB
* 3 BigEndian LZ4
* 4 LittleEndian NONE
* 5 LittleEndian ZLIB
* 6 LittleEndian LZ4
* @endcode
*
* thus exploring the entire parameter space.
*
* A unique, human-readable description of the current configuration can be
* obtained with GetCurrentPermutationName() as long as IsDoneWithPermutations()
* returns false. E.g. the third iteration will be named
* "ByteOrder.BigEndian-CompressorType.LZ4".
*/
template <typename ObjType>
class vtkPermuteOptions
{
using Permutation = std::vector<size_t>;
struct Value
{
Value(const std::string& name, std::function<void(ObjType*)> setter)
: Name(name)
, Setter(setter)
{
}
void Apply(ObjType* obj) const { this->Setter(obj); }
std::string Name; // user-readable option name
std::function<void(ObjType*)> Setter; // Sets the option to a single values
};
struct Option
{
Option(const std::string& name)
: Name(name)
{
}
std::string Name; // user-readable option name
std::vector<Value> Values; // list of values to test for this option
};
std::vector<Option> Options;
std::vector<Permutation> Permutations;
size_t CurrentPermutation;
vtkTimeStamp OptionTime;
vtkTimeStamp PermutationTime;
Option& FindOrCreateOption(const std::string& name)
{
for (Option& opt : this->Options)
{
if (opt.Name == name)
{
return opt;
}
}
this->Options.emplace_back(name);
return this->Options.back();
}
void RecursePermutations(Permutation& perm, size_t level)
{
const size_t maxIdx = this->Options[level].Values.size();
if (level == 0) // base case
{
for (size_t i = 0; i < maxIdx; ++i)
{
perm[0] = i;
this->Permutations.push_back(perm);
}
}
else // recursive case
{
for (size_t i = 0; i < maxIdx; ++i)
{
perm[level] = i;
this->RecursePermutations(perm, level - 1);
}
}
}
void RebuildPermutations()
{
this->Permutations.clear();
const size_t numOptions = this->Options.size();
Permutation perm(numOptions, 0);
this->RecursePermutations(perm, numOptions - 1);
this->PermutationTime.Modified();
}
void Apply(ObjType* obj, const Permutation& perm) const
{
const size_t numOpts = this->Options.size();
assert("Sane permutation" && perm.size() == numOpts);
for (size_t i = 0; i < numOpts; ++i)
{
size_t valIdx = perm[i];
assert("ValueIdx in range" && valIdx < this->Options[i].Values.size());
this->Options[i].Values[valIdx].Apply(obj);
}
}
std::string NamePermutation(const Permutation& perm) const
{
const size_t numOpts = this->Options.size();
assert("Sane permutation" && perm.size() == numOpts);
std::ostringstream out;
for (size_t i = 0; i < numOpts; ++i)
{
size_t valIdx = perm[i];
assert("ValueIdx in range" && valIdx < this->Options[i].Values.size());
out << (i != 0 ? "-" : "") << this->Options[i].Name << "."
<< this->Options[i].Values[valIdx].Name;
}
return out.str();
}
public:
vtkPermuteOptions()
: CurrentPermutation(0)
{
}
template <typename SetterType, typename ValueType>
void AddOptionValue(
const std::string& optionName, SetterType setter, const std::string& valueName, ValueType value)
{
using std::placeholders::_1;
std::function<void(ObjType*)> func = std::bind(setter, _1, value);
Option& opt = this->FindOrCreateOption(optionName);
opt.Values.emplace_back(valueName, func);
this->OptionTime.Modified();
}
template <typename SetterType, typename ValueType>
void AddOptionValues(
const std::string& optionName, SetterType setter, const std::string& valueName, ValueType value)
{
this->AddOptionValue(optionName, setter, valueName, value);
}
template <typename SetterType, typename ValueType, typename... Tail>
void AddOptionValues(const std::string& optionName, SetterType setter,
const std::string& valueName, ValueType value, Tail... tail)
{
this->AddOptionValue(optionName, setter, valueName, value);
this->AddOptionValues(optionName, setter, tail...);
}
void InitPermutations()
{
if (this->OptionTime > this->PermutationTime)
{
this->RebuildPermutations();
}
this->CurrentPermutation = 0;
}
bool IsDoneWithPermutations() const
{
assert("Modified options without resetting permutations." &&
this->PermutationTime > this->OptionTime);
return this->CurrentPermutation >= this->Permutations.size();
}
void GoToNextPermutation()
{
assert("Modified options without resetting permutations." &&
this->PermutationTime > this->OptionTime);
assert("Invalid permutation." && !this->IsDoneWithPermutations());
++this->CurrentPermutation;
}
void ApplyCurrentPermutation(ObjType* obj) const
{
assert("Modified options without resetting permutations." &&
this->PermutationTime > this->OptionTime);
assert("Invalid permutation." && !this->IsDoneWithPermutations());
this->Apply(obj, this->Permutations[this->CurrentPermutation]);
}
std::string GetCurrentPermutationName() const
{
assert("Modified options without resetting permutations." &&
this->PermutationTime > this->OptionTime);
assert("Invalid permutation." && !this->IsDoneWithPermutations());
return this->NamePermutation(this->Permutations[this->CurrentPermutation]);
}
};
#endif
// VTK-HeaderTest-Exclude: vtkPermuteOptions.h