If you write generators using testlib, you can probable feel the inconvenience to write as described in the official documentation:
int main(int argc, char *argv[]) {
registerGen(argc, argv, 1);
int n = atoi(argv[1]);
int m = atoi(argv[2]);
...
And, if your generator has too many parameters, you should remember what each parameter is used for.
ParmPars makes it easier to parse command line parameters. Consider this example:
int main(int argc, char *argv[]) {
initGenerator(argv[1]);
DECLARE(int, n, range(1, 1'000'000));
DECLARE(int, m, range(1, n));
...
The generator can be invoked using, e.g., the following line: ./gen n=10 m=3
. This way of invoking and writing generator looks more clean and less error-prone. You won't forget which parameter is used for what. And using DECLARE_D
, you can add default values for your parameters, so decreasing the amount of them in the command line.
-
Error reporting (e. g. when a parameter was passed and not used in the program)
-
Parameter validation
-
Generative parameters (see [#usage](the usage below))
-
Preprocessor and constant substitution
-
Independence from Testlib (though using ParmPars together with Testlib adds features like generative parameters)
-
Still alpha, may contain bugs
-
May compile slowly
-
Too many templates
They allow to extend the library for the new types and validations easily. They make the architecture cleaner and prevent from copy-pasting such methods as getting the value of the variable for different types.
To use ParmPars, just
#include "parmpars.hpp"
If you also use Testlib, place this include after #include "testlib.h"
.
You may add some macros to tweak the behaviour of ParmPars.
All the parameters should have form <name>=<value>
. The last parameters may be a seed and contain only alphanumerical cha
parser.init(int argc, char *argv[])
: must be called at startup. Initializes the parser.initGenerator(int argc, char *argv[], int randomGeneratorVersion)
: Testlib required, runs bothregisterGen
andparser.init
.DECLARE(type, name, a...)
: declare a variable of typetype
and namename
(which value is read from the parameters). Additional validation arguments can be supplied (see below). If the variable not found, exit with error.DECLARE_D(type, name, def, a...)
: same as previous, but if the variable not found, use the default value.
The following types are supported:
- Any integers
float
,double
,long double
bool
,char
std::string
-
For any integer and floating point types (including
bool
andchar
), range validation can be used. It can be used like this:DECLARE(int, n, range(1, 100));
If
n
is outside of range[1; 100]
, the generator will throw the error. -
For string regex-based validation can be used. It looks in the following way:
DECLARE(string, s, "[a-z]{1,100}");
Validation also applies to DECLARE_D
.
You can use the macros to control the features of ParmPars. The following macros can be used:
PARMPARS_EXIT_ON_WARNING
: treats warnings as errors and exit the generator on each warning.PARMPARS_USE_REGEX
: usestd::regex
to process regex validation. Disabled by default as not all compilers may support it. Also using STL regex increases compilation time. IfPARMPARS_USE_REGEX
is not defined, Testlib's regex implementation is used (if it was included)
This works only with Testlib. Currently, there are two classes for this:
GenRange<T>
: generates a number from the specified range.GenRegex
: generates a string using regex.
How this works? Suppose you need to tell the generator that n
must be from 1
to 10
, but want to give the ability for the generator to choose the exact value. You can write this:
int main(int argc, char *argv[]) {
initGenerator(argc, argv, 1);
DECLARE_GEN(GenRange<int>, n);
...
Now run this generator as ./gen n=[1,10]
. It will read [1,10]
as GenRange<int>
and use it to generate a number from 1
to 10
, and then declare int n
with this value. ParmPars accepts different syntaxes for generator ranges: round brackets are also acceptable, and ;
can be used as a separator instead of ,
. It can be helpful, for example, when used with Polygon's stress tests.
You can also use a longer way:
...
DECLARE(GenRange<int>, n);
int nGen = n.generate();
...
So, DECLARE_GEN
is a shortcut to read GenRange<T>
from the parameters and generate a value using one macro.
DECLARE_GEN_D
does the same thing as DECLARE_GEN
, but you can specify the default GenRange<T>
which can be used for generating if the variable was not found.
GenRange<T>
also supports range validation, it will validate not the generated value, but the range read from the input.
Tired of using something like n=1000000
in all of your tests? Then, constant substitution if for you. ParmPars has a simple preprocessor. To define a constant, you can use something like
param.define("n", 1000000);
When you DECLARE
a parameter generator, ParmPars will use all the constants declared before.
The syntax of the preprocessor is quite simple. The constant should be used in the parameter as @constName
or @constName!
, the latter variant can be used to avoid confusion when the next character after the constant is a letter or number. If the variable name if empty, it will substitute to @
character.
Examples:
Suppose we have these lines in test.cpp
:
...
param.define("c", "const");
DECLARE(string, s)
cout << "s = " << s << endl;
...
If we compile and run the executable, we will get:
test.exe s=@c
=>s = const
test.exe s=@c1
=> Error, as ParmPars will try to search for constantc1
test.exe s=@c!1
=>s = const1
test.exe s=@
=>s = @
test.exe s=@!c
=>s = @c
test.exe s=@c+@c
=>s = const+const
test.exe s=@c!+@c!
=>s = const+const
See also test_macros.cpp
.
See test.cpp
.
Testlib is heavy enough, you may choose not to use it for your generator, but still use ParmPars. Also, you can use alternative libraries for generators, like jngen, and use ParmPars with it.
ParmPars is distributed under MIT license, like Testlib.