Overview
What is CPLib?
CPLib is a library written in C++ for processing test data of competitive programming problems. It helps you write clear and efficient checkers, interactors, validators, and generators.
CPLib embraces a structured and modular approach, centered around Input
and Output
structs. This design paradigm enhances clarity, maintainability, and testability of your problem logic.
Each checker defines:
- An
Input
struct with a staticread
method to parse problem input. - An
Output
struct with a staticread
method to parse participant/jury output and perform basic format validation, and a staticevaluate
method that takes anevaluate::Evaluator
, participant's output, jury's output, and the original input to compare results and determine correctness.
Here is a basic example of an A+B problem checker using CPLib:
#include "cplib.hpp"
using namespace cplib;
// Define the Input struct to read problem-specific inputs (A and B for A+B problem)
struct Input {
int a, b;
static Input read(var::Reader& in) {
// Read integer 'a' within range [-1000, 1000]
auto a = in.read(var::i32("a", -1000, 1000));
// Read integer 'b' within range [-1000, 1000]
auto b = in.read(var::i32("b", -1000, 1000));
return {a, b};
}
};
// Define the Output struct to read the answer and evaluate it
struct Output {
int ans;
// Static read method to parse participant/jury output
// It receives a var::Reader and the Input struct for context if needed.
static Output read(var::Reader& in, const Input&) {
// Read integer 'ans' within range [-2000, 2000]
auto ans = in.read(var::i32("ans", -2000, 2000));
return {ans};
}
// Static evaluate method to compare participant's output with jury's output
// It receives an evaluate::Evaluator, participant's output (pans),
// jury's output (jans), and the original Input.
static evaluate::Result evaluate(evaluate::Evaluator& ev, const Output& pans, const Output& jans,
const Input&) {
// Use ev.eq to compare the 'ans' field.
// If pans.ans == jans.ans, it contributes to AC. Otherwise, it marks WA.
auto res = evaluate::Result::ac();
res &= ev.eq("ans", pans.ans, jans.ans);
return res;
}
};
// Register the checker with CPLib, specifying the Input and Output structs.
// This macro sets up the main checker logic behind the scenes.
CPLIB_REGISTER_CHECKER(chk, Input, Output);
This example demonstrates how CPLib now leverages structured data types (Input
, Output
) and dedicated read
/evaluate
methods to clearly separate concerns: input parsing, output parsing, and result evaluation. The evaluate::Evaluator
provides a powerful and flexible way to compare results, handle floating-point precision, and provide detailed feedback in case of errors.
Why use CPLib?
The advantages of the CPLib project over other similarly positioned libraries are mainly reflected in the following points.
Modernization
CPLib is developed using the C++ 20 standard. The standard library and features of modern C++ offer CPLib numerous syntactic sugars and enable fast implementation. Simultaneously, CPLib prioritizes utilizing the standard library instead of creating separate implementations, ensuring better compatibility with other libraries.
CPLib also has more modernized handling of some functional details. For example, when an input error occurs, CPLib returns a complete input stack trace, and when reporting output to a file, it uses the more popular JSON format instead of the XML format used by Testlib.
Structured Design & Extensibility
CPLib's extensibility and robust design are primarily reflected in two key areas: the "variable input template" (cplib::var::Var
) and the powerful evaluate::Evaluator
system, alongside customization for various evaluation platforms.
Variable Input Template (cplib::var::Var
)
CPLib's variable input is based on the "variable input template", rather than Testlib-style readXYZ()
series of functions that apply to various basic types. This makes it more convenient for CPLib to extend and combine input types, making the program more modular and readable.
CPLib's design encourages users to split complex problem inputs and outputs into several dedicated structures that implement static read
functions. This approach simplifies the code and makes it easier to understand.
Here is a simple example that shows how to read a directed graph by combining and extending cplib::var::Var
within custom structs:
struct Edge {
int32_t from, to;
static auto read(var::Reader &in, int32_t n) -> Edge {
auto [from, to] = in(var::i32("from", 1, n), var::i32("to", 1, n));
if (from == to) in.fail("Self-loops are not allowed");
return {from, to};
}
};
struct Graph {
int32_t n, m;
std::vector<Edge> edges;
static auto read(var::Reader &in) -> Graph {
auto [n, m] = in(var::i32("n"), var::i32("m"));
auto edges = in.read(var::ExtVar<Edge>("edge", n) * m);
return {n, m, edges};
}
};
Powerful Evaluation (evaluate::Evaluator
)
Beyond input/output parsing, CPLib introduces a dedicated evaluate::Evaluator
system for defining problem-specific correctness, allowing for:
- Clear Separation of Concerns: Evaluation logic resides in a dedicated
Output::evaluate
static method, distinct from input/output parsing. - Flexible Comparison Methods: The
Evaluator
provides methods likeev.eq()
for exact comparisons (integers, strings, vectors) andev.approx()
for floating-point comparisons with customizable error margins. - Detailed Feedback: It enables rich error reporting with trace stack support, allowing you to specify which specific part of the output is incorrect and provide context-specific messages.
On the other hand, CPLib takes into account the differences in some interfaces of various evaluation platforms and allows the use of initializer
and reporter
to customize the program's initialization and reporting methods. This way, you can achieve compatibility with upper-level evaluation platforms without modifying the CPLib source code.
Efficiency
The test environment is as follows:
- CPU: AMD Ryzen 7 6800H @ 3.2GHz.
- OS: openSUSE Linux.
- Compiler: g++ (GCC) 12.3.0.
- Compiler flags:
-O2
. - C++ Standard Library: libstdc++.
After rounds of testing, Testlib efficiency was recorded as 100%, and CPLib's corresponding efficiency was calculated based on this value.
The dataset used for testing can be found in the GitHub repository (opens in a new tab).
Applicable Environments
CPLib provides assistance in four environments:
- Checker: Checks answers of problem with multiple correct cases or detailed scoring requirements.
- Interactor: Interact with participant's program in interactive problems.
- Validator: Validates test input files with per-character precision in strict mode.
- Generator: Generates data in batches.
Got Questions?
Please refer to our FAQ.