November 27, 2016


See the migration guide from 0.x at the bottom of this page.

First stable release. Almost a complete rewrite from 0.x. Focusing on syntax improvements and conceptual refinements.


  • Minimum GCC version is now 4.8.
  • The source file of a runner program is now conceptually called “spec file”, and is called spec.cpp instead of runner.cpp.
  • A spec file now includes <tcframe/spec.hpp> instead of <tcframe/runner.hpp>.
  • main() function in a spec file is not needed and must be removed.
  • Problem specification is now called a “problem spec”, and its class is ProblemSpec instead of Problem. It now inherits BaseProblemSpec.
  • Generator specification is now called a “test spec”, and its class is TestSpec instead of Generator. It now inherits BaseTestSpec.
  • The container directory of a spec file is now conceptually called “problem package directory”.
  • The slug for a problem is now taken from the name of the problem package directory, and cannot be overridden via problem spec or command-line option.
  • Config() in test spec is removed, so it is not possible to e.g. specify the solution command and test cases output directory in test spec.
  • Config() in problem spec is split into GradingConfig() and MultipleTestCasesConfig().
  • setTimeLimit() and setMemoryLimit in problem spec’s Config() are now TimeLimit() and MemoryLimit() in GradingConfig().
  • setMultipleTestCasesCount() in problem spec’s Config() is now Counter() in MultipleTestCasesConfig().
  • assignToSubtasks() in test groups is now Subtasks().
  • Individual sample test cases now live in their own methods, SampleTestCaseX() where X is sample test case number.
  • SAMPLE_CASE() is split into multiple calls: Subtasks(), Input(), and Output().
  • FinalizeInput() is renamed to AfterTestCase().
  • “Submission simulation” is now conceptually called “local grading”.
  • ./runner submit is now ./runner grade.
  • --brief option for local grading is removed until there is clear use case for it.
  • --slug option is removed.
  • --solution-command option is simplified to --solution.
  • --tc-dir option is renamed to --output.

New features

  • tcframe helper script is introduced. It currently has 2 commands: tcframe build, which compile a spec file into a runner program, and tcframe version, which prints current tcframe version.
  • A summary is printed after generation/grading, which shows whether or not there are failing test cases.
  • It is now possible to specify the literal output for sample test cases, by calling Output() inside SampleTestCaseX().
  • BeforeTestCase() to complement the new AfterTestCase(), which can be used to initialize input variables before each test case.
  • For problems with subtasks, Constraints() can be used to specify global constraints that apply to every subtask.
  • In ICPC-style multiple test cases in a file problems, it is now possible to specify a prefix that will be prepended to every output, by calling OutputPrefix() (e.g. OutputPrefix("Case #%d ");).


  • Maximum X for TestGroupX(), SubtaskX() (and the new SampleTestCaseX()) is now 25.
  • If there are errors in I/O format, previously the error was shown in every test case, resulting in spammy output. Now, it is reported only once before the test cases are evaluated.


  • When using multiple test groups in ICPC-style multiple test cases in a file problems, it was erroneously required to call assignToSubtasks({-1}). Now, it is not required.
  • In e.g. this input segment LINE(A % SIZE(3), B), when B is not a supported type, tcframe reported the error as Variable type of `%` unsatisfied... due to a tokenization bug. It is now fixed.

Project development

  • Repository moved to https://github.com/tcframe/tcframe.
  • (Almost) every class now lives on its own file.
  • Rewrite almost everything with better testability in mind. Now, we have decent unit tests.
  • Use Google Test and Google Mock for unit tests.
  • Add basic end-to-end tests for generation and local grading.


First, “install” the tcframe CLI utility; see Installation for more details.

Then, apply the following for each runner.cpp you want to migrate.


  • Rename runner.cpp to spec.cpp.

  • Include <tcframe/spec.hpp> instead of "tcframe/runner.hpp".

  • Completely remove main() function.

  • Change the following two classes

    class Problem : public BaseProblem {};
    class Generator : public BaseGenerator<Problem> {};


    class ProblemSpec : public BaseProblemSpec {};
    class TestSpec : public BaseTestSpec<ProblemSpec> {};


  • Remove setSlug() from Config(). The slug is now inferred from the containing (problem package) directory. See Problem package for more details.

  • Change the following:

    void Config() {


    void MultipleTestCasesConfig() {
    void GradingConfig() {


  • Completely remove Config() – the options in it should now be specified via command-line options instead.
  • Change assignToSubtasks() to Subtasks().
  • Change FinalizeInput() to AfterTestCase().
  • Split SampleTestCases() into multiple SampleTestCaseX(). See Test cases for more details.

After migration

You should now be able to run tcframe build to compile your new spec file into runner program.

The following are new in 1.0.0 that are recommended to do:

  • Put all input variable initializations in BeforeTestCase() instead of repeating them in private helper methods. See Test case lifecycle for more details.
  • Put all global problem constraints in Constraints() instead of repeating them in all SubtaskX()s, if your problem has subtasks.
  • Include Output() in sample test case definitions, so that you are sure you are typing the output correctly in the problem statement by only looking at the spec file (no need to check with the actual produced output file).