CP++ is a domain-specific language for competitive programming test generation and solution verification. It is designed for problem setters who need to create valid test data, produce official outputs, and optionally compare an alternative solution against the official solution.
The DSL describes a problem's test configuration and input generation logic in one .CPPP file. CP++ parses that file, builds an AST, validates variable usage, generates a C++ test generator, runs the official solution, and optionally runs an alternative solution for comparison.
- Java runtime for ANTLR generation.
- Python 3.9.
- C++ compiler.
Regenerate the ANTLR parser after changing grammar/CPPP.g4.
python run.py genRun CP++ on a DSL file. Only .CPPP files are accepted.
python run.py test tests/testcase.CPPPPRIME1 {
config {
input "PRIME1.inp"
output "PRIME1.out"
tests 10
sol "tests/sol.cpp"
test_sol "tests/test_sol.cpp"
compare 5000
}
generate {
var int t range(1, 5);
print t;
repeat(t) {
var ll n range(10000000000, 1000000000000000000);
print n;
}
}
}This creates 10 test folders for PRIME1. Each test contains generated input and official output from sol.cpp. Because compare 5000 is present, CP++ also runs test_sol.cpp on the same input with a 5000ms time limit, then compares its output with the official output;
A .CPPP file contains one or more subtask blocks.
program := subtaskBlock+
subtaskBlock := subtaskName "{" configBlock genBlock "}"
subtaskName := ID+Each subtask has a config block and a generate block.
config {
}
generate {
...
}config fields:
input: generated input file name for each test.output: official output file name for each test.tests: number of test cases to generate.sol: official C++ solution used to produce expected output.test_sol: alternative C++ solution used for comparison.compare: optional flag. If present, CP++ runstest_soland compares it againstsol(an optional value aftercompareis used as the time limit in milliseconds.)
Generation statements:
var type name options;
var array<type> name[size] options;
print expr;
repeat(expr) {
...
}Supported primitive types:
intllfloatdoublecharstring
Supported compound types:
array<T>tree(expr)graph(expr, expr)
Tree and graph generation:
var tree(n) tr;
print tr;
var graph(n, m) g simple connected;
print g;tree(n)generatesn - 1edges as pairsu v.graph(n, m)generatesmedges as pairsu v.print trprints each tree edge on its own line.print gprints each graph edge on its own line.
Options are written after a variable declaration.
var int n range(1, 100000);
var array<int> a[n] distinct sorted range(1, 1000000000);Supported options:
range(l, r): sets the random range.distinct: array values must be unique.sorted: array values are sorted after generation.distince: accepted as a typo-compatible alias fordistinct.simple: graph has no self-loops and no duplicate edges.connected: graph starts from a generated spanning tree before adding random edges.directed: graph duplicate checks use ordered pairs.weighted: graph edges include a random weight.
Range behavior:
- For
int,ll,float, anddouble,range(l, r)controls the generated numeric value. - For
char,range(l, r)controls ASCII code. The default is97..122, which is lowercasea..z. - For
string,range(l, r)controls string length. Characters are generated from lowercasea..z. - For arrays,
range(l, r)controls each generated element.
Graph option behavior:
simple connectedis useful for most undirected connected graph tests.weightedprints graph edges asu v w.- Without
weighted, graph edges are printed asu v. - For a simple undirected graph, make sure
m <= n * (n - 1) / 2; otherwise generation may not terminate. - For a simple directed graph, make sure
m <= n * (n - 1).
For each subtask, CP++ creates files under tests/<subtask_name>/.
tests/
PRIME1/
gen.cpp
test1/
PRIME1.inp
PRIME1.out
test_sol.outtest_sol.out is only generated when compare is present in the config block.
