You can refer to the second introduction post here.
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 uses "variable input template" as its major feature, provides friendly feedback for humans and backends, and is working hard to reach better compatibility and efficiency.
You can get CPLib from Github Repository. The "single-header-snapshot" branch automatically updates single-header version of CPLib which is easy to use. Also, you can visit cplib.vercel.app for more information. For regex-related questions, please refer to the FAQ page.
Here is a basic example of a checker using CPLib. For more examples, visit the links above.
#include "cplib.hpp"
using namespace cplib;
CPLIB_REGISTER_CHECKER(chk);
void checker_main() {
auto var_ans = var::i32("ans", -2000, 2000);
int ouf_output = chk.ouf.read(var_ans);
int ans_output = chk.ans.read(var_ans);
if (ouf_output != ans_output) {
chk.quit_wa(format("Expected %d, got %d", ans_output, ouf_output));
}
chk.quit_ac();
}
The project is mainly written by yzy1, and I, tiger2005, work on syntax designs and some tiny stuffs. The project is still under development, so feel free to make suggestions!
CPLib welcomes contributions from the community! Feel free to star & fork & issue & pr!
What's wrong with testlib since this project exists?
Any success with creating problem in polygon? Or there is also chinise analog of polygon?
Why c++ but not rust?
The code in testlib is full of historical legacy factors. For example, when in app mode, the XML output defaults to using a Russian encoding instead of UTF-8.
Testlib has poor compatibility with higher-level testing systems. If you search for testlib on GitHub, you will find versions like testlib for lemon, testlib for DOMJudge, testlib for LibreOJ, and others that are compatible with various higher-level testing systems. In other words, if you want to make testlib compatible with a new testing system, you need to modify the entire header file of testlib, and these modified versions of testlib are based on different versions of the original testlib, which may result in API compatibility issues. However, it should be noted that the differences between these testing systems only lie in the command-line parameters used when invoking the testlib program, the locations of files or streams to be read or written, and the format of the report. On the other hand, by using CPLib, you only need to write an initializer for each testing system to adapt to multiple testing systems without changing the CPLib header file.
The variable reading API in testlib is too simple. Testlib only provides functions to read integers/longs/strings and their corresponding vector types. In contrast, the variable reading template design in CPLib allows you to combine and reuse variables (you can refer to the documentation here).
CPLib has optimized some of the more cumbersome APIs in testlib. For example, when writing a generator, the
opt<T>("name", default)
syntax in testlib can easily cause problems if the name is misspelled, while the corresponding design in CPLib first binds the command-line parameters to a structure before parsing, which can effectively avoid such problems.The pattern functionality in testlib is broken, and the syntax for alternative sets is incomplete. For example,
id-(aa|bb)
can be correctly parsed in testlib, but(aa|bb)-id
cannot.As for why not use Rust, although Rust is a good programming language, I believe that over 95% of algorithmic competition participants who are skilled enough to create problems are proficient in C++, while the number of those who can master Rust is probably less than half.
Please explain more about generator problem. Testlib-generator will crash if argument are 1) not exist in args 2) are not used in generator. So what you mean by misspelled?
Also example in link looks verbose comparing to testlib, especially this
args.
and.value
everywhereJust like in this example:
The design of testlib is to provide a string representing the name of the parameter every time it is needed on the command line, without any checking. This may lead to more errors compared to first defining all the parameters and then using them.
As for the issue with
args.
and.value
, it is necessary to keepargs.
because nobody wants to pollute the global namespace with a bunch of global variables or add uglyargs_xyz
prefixes to all parameter variables. However, if you find the nameargs
too long, you can also rename it when defininggenerator_main
. As for.value
, the initial design was to overload theoperator *
to achieve the operation of taking the value (just like pointers), but I don't really like overloading operators that have different meanings from the original ones (It's weird to overloadoperator *
for something that's not a pointer). so in the end, I chose the syntax of.value
.I finally got it — its about default parameters (I personally rarely using it). So it is good example.
But this is also good example of bad programming — typing same code twice... Especially code wich potentially will be changed. So so.
Looking at my typical generators I see other not comfortable thing. I am coding all generators in one file, so typical code is like
In each if a can request additional arguments. So create general class is messy and dangerous here.
In short, our goal in developing CPLib is similar to that of the typst team's development of typst a year ago: why, when there was already LaTeX at the time, did someone still develop typst?
Just as typst aimed to provide a simpler and more user-friendly interface for LaTeX users, CPLib aims to provide a more modern, flexible, and easy-to-use library for algorithmic competition problem setters. While testlib has been widely used in the past, it has several issues that CPLib seeks to address. By providing a more powerful and user-friendly library, we hope to make the process of creating high-quality problems for algorithmic competitions more accessible and enjoyable for everyone.
cool
Does this library guarantees the same result of generated data on different compiler versions / backends (like clang, gcc)?
It seems like no. It is just a wrapper around the STL distributions which are implementation-defined behaviour.
Yes, it guarantees the same result of generated data on different compiler. Unless your code has undefined behavior, only the functions or classes used for randomization (rand(), mt19937) will affect the output of your code.The design of CPLib prohibits you from using random functions in the standard library such as std::rand, and instead uses cplib::Random. Under generator, the seed of cplib::Random is only related to the command line parameters of generator.
EDIT: There are problems with the current implementation, and this is not what we expected to see. The problem will be fixed in the next few commits.
EDIT 2: Fixed in latest commit!
You use
IntegerDist = std::uniform_int_distribution<T>;
right?std::uniform_int_distribution<T>
is implementation defined behavior and will (can) therefore behave differently depending on compiler and version. GCC just recently (with version 11) changed the implementation of this distribution. (In fact the c++ standard gives almost no guarantees for the distributions besides that they are unbiased).Thank you for your feedback. We didn't notice this problem when implementing this part. The current effect is also inconsistent with the design of CPLib to eliminate platform independence. We will fix this problem in the next few commits.
Fixed in latest commit!
It has been tested on Linux GCC (libstdc++) & Clang (libc++) and Windows GCC & MSVC and been confirmed that the same output can be obtained. And later, I will try how to use CI to automate the testing process on different platforms.
Finally, thank you for your contribution in pointing out potential bugs in this project! CPLib developers will continue to pay attention to issues from the community.
Cool!
Why didn't you collab with Polygon team? Do you plan to make CPLib the default choice in Polygon?
Really great job! thanks for sharing it with us. Is there a list of all possible gen.rnd.next functions like the one for testlib ? Thanks
The function
gen.rnd.next
has three overloaded versions: zero parameters, one parameter, or two parameters. The most commonly used version is the one with two parameters, which is used to generate a uniformly random integer or real number within a specified range. For example,gen.rnd.next(1,5)
will generate an integer within the range [1,5], andgen.rnd.next('A','Z')
will generate a character within the range ['A','Z']. The return type of the result will be automatically inferred based on the types of the parameters. In some cases, you can also manually specify the type by using something likegen.rnd.next<double>(1,5)
.When only one parameter is provided, it accepts a floating-point number
true_prob
and returns a bool type. The probability of the result being true istrue_prob
, and the probability of it being false is1-true_prob
.When zero parameters are provided, it is equivalent to
gen.rnd.next(0.5)
, meaning it will randomly return true or false with a 50% probability each.Hi, I followed the steps in your documentation for the checker example code, Currently in empty directory I placed cplib.hpp, chk.cpp and data folder mentioned in the doc., but after compiling the chk.cpp, I am getting an error on cplib.hpp file. Please help with this, I don't know if I am doing something wrong?
Can you provide a more detailed description of the environment, such as operating system type, architecture, compiler type and version?
I am currently using WSL2 in my Windows11, Basically Ubuntu 22.04.3 LTS (GNU/Linux 5.15.133.1-microsoft-standard-WSL2 x86_64). Compiler I am using g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0.
This seems to be due to issues with the handling of friend functions and templates in older versions of gcc (lower than 11).
The latest version commit fixes this problem and now it works on versions as low as gcc8.
Cool, It worked. Although I will be starting to use g++-13 cause the previous version of your lib worked fine.
I also liked your lib. It has some great and interesting features. Kudos.
Is it something wrong with WSL2?, I ran with same configuration in my Windows compiler. It is working fine, just had to update regex.h file as shown in FAQ.
How would I use this on Polygon? I think
regex.h
doesn't exist on Polygon forg++
and I am not sure how to add it to the system include path there.https://github.com/rindag-devs/musl-regex-standalone Add this file in Polygon file system and cplib should work.
I already tried adding it but the source in
cplib.hpp
uses#include <regex.h>
, and I think that doesn't look for local paths, only system paths.I can get it to work by changing all
#include <regex.h>
s incplib.hpp
to#include "regex.h"
, uploading the customregex.h
, and using winlibs gcc for source files.This is because
regex.h
you added is a custom header. In fact,regex.h
is implemented for Linux platform, and it has good performance, but it's somehow difficult to useregex.h
in Windows, and the official<regex>
seems too slow. We will find out a suitable way to importregex
later on.When using on Polygon, a validator gives an error of "Validator 'validator.exe' returns exit code 1 [{"status": "internal_error", "message": "Unknown option: --testset"}]".
The issue is mainly caused by additional parameters from Polygon itself. We are working to make CPLib compatible with Polygon, but this will take some time because of personal schedules.
Good news! We are now compatible with Codeforces Polygon and other online judge platforms that use testlib.
See Introduction to CPLib (2) for details.
Auto comment: topic has been updated by tiger2005 (previous revision, new revision, compare).