How to Use a Custom Main Function with Catch2: Four Implementation Patterns
To use a custom main function with Catch2, include <catch2/catch_session.hpp> and instantiate Catch::Session to manually control test execution, configuration, and command-line parsing instead of linking against the default Catch2Main library.
While the catchorg/Catch2 framework provides a default main() function via the Catch2Main library, many projects require custom initialization, teardown, or specialized command-line handling. By using the Catch::Session class defined in src/catch2/catch_session.hpp, you can replace the default entry point while retaining full access to Catch2's test runner and configuration system.
Understanding the Catch::Session API
The Catch::Session class serves as the primary interface for custom main implementations. According to the Catch2 source code, this class encapsulates the configuration state (ConfigData), the Clara-based command-line parser, and the run() method that executes the test suite. A single Session instance must exist throughout the application lifetime, managing the transition from argument parsing to test execution through methods like applyCommandLine() and runInternal().
Key methods exposed in src/catch2/catch_session.hpp include:
run(int argc, char const* argv[])- Single-step initialization and executionapplyCommandLine(int argc, char const* argv[])- Parses arguments without running testsconfigData()- Returns a mutable reference to the underlyingConfigDatastructurecli()- Gets or sets the Clara command-line parser instanceuseConfigData(ConfigData const&)- Bypasses CLI parsing entirely
Basic Custom Main Implementation
The simplest pattern forwards command-line arguments directly to Catch2 while wrapping the test run with custom setup and teardown logic.
#include <catch2/catch_session.hpp>
int main(int argc, char* argv[]) {
// Optional pre-test setup
int result = Catch::Session().run(argc, argv);
// Optional post-test cleanup
return result;
}
This creates a temporary Session object and invokes run(int, char const*[]) (lines 30-53 in catch_session.hpp), which internally calls applyCommandLine() followed by runInternal(). Use this approach when you need to execute code before tests start or after they complete, but do not need to modify Catch2's configuration.
Modifying Configuration After Parsing
For scenarios requiring programmatic configuration adjustments after CLI parsing, instantiate a persistent Session object and manipulate its ConfigData before calling run().
#include <catch2/catch_session.hpp>
int main(int argc, char* argv[]) {
Catch::Session session; // Exactly one instance required
// Set defaults before CLI parsing
session.configData().verbosity = Catch::Verbosity::High;
int rc = session.applyCommandLine(argc, argv);
if (rc != 0) return rc; // Handle CLI errors
// Override configuration after parsing
session.configData().abortAfter = 5; // Stop after 5 failures
return session.run();
}
The applyCommandLine() method (line 36) populates the internal ConfigData structure from argc/argv. After this returns successfully, you can modify values through configData() (accessor at line 58) or replace the entire configuration object to customize test behavior without changing command-line arguments.
Adding Custom Command-Line Options
To extend Catch2's built-in argument parser with project-specific options, access the Clara parser via cli() and compose additional flags using the pipe operator.
#include <catch2/catch_session.hpp>
#include <iostream>
int main(int argc, char* argv[]) {
Catch::Session session;
int height = 0; // User-defined variable
using namespace Catch::Clara;
auto cli = session.cli() |
Opt(height, "height")
["-g"]["--height"]
("How high?");
session.cli(cli); // Replace parser with extended version
int rc = session.applyCommandLine(argc, argv);
if (rc != 0) return rc;
if (height > 0)
std::cout << "height: " << height << '\n';
return session.run();
}
The Session class exposes its Clara parser via cli() const (line 55) and accepts modifications through cli(Clara::Parser const&) (line 56). By OR-ing (|) a new Opt object, you extend the command line without invalidating Catch2's core parser functionality.
Bypassing Command-Line Parsing Entirely
For embedded environments or fixed test configurations where command-line arguments are unavailable, construct a ConfigData object programmatically and inject it directly into the session.
#include <catch2/catch_session.hpp>
int main() {
Catch::Session session;
// Build configuration manually
Catch::ConfigData cfg;
cfg.shouldThrow = false;
cfg.verbosity = Catch::Verbosity::Quiet;
session.useConfigData(cfg); // Bypass CLI completely
return session.run();
}
Calling useConfigData() (line 41) replaces the automatically built configuration, allowing you to skip applyCommandLine() entirely. This pattern is documented in docs/own-main.md and is particularly useful when integrating Catch2 into applications with constrained execution environments.
Summary
- Include
<catch2/catch_session.hpp>(orextras/catch_amalgamated.hppfor single-header builds) to access theCatch::Sessionclass - Link against the
Catch2target instead ofCatch2Mainwhen providing your ownmain()function - Use
session.applyCommandLine()to parse arguments, then modifysession.configData()for post-parse configuration changes - Extend the CLI parser by composing
Catch::Clara::Optobjects withsession.cli() - Call
session.useConfigData()to bypass command-line parsing entirely for fixed configurations
Frequently Asked Questions
Do I need to link against Catch2Main when using a custom main function?
No. When providing your own main() function, link against the Catch2 static library target instead of Catch2Main. The Catch2Main library exists solely to supply the default entry point, while Catch2 contains the test framework and Session class without a predefined main().
Can I use the amalgamated header with a custom main implementation?
Yes. The extras/catch_amalgamated.hpp file contains the complete Catch::Session implementation and all related configuration classes. Include this single header instead of the modular headers when using the amalgamated distribution of Catch2.
How do I handle command-line parsing errors in a custom main?
Check the integer return value from session.applyCommandLine(argc, argv). If this method returns non-zero, it indicates a CLI parsing error (such as unknown arguments), and you should return this value immediately to propagate the error to the shell without attempting to run tests.
Is it safe to modify ConfigData after calling applyCommandLine?
Yes. You can modify the configuration via session.configData() after applyCommandLine() returns successfully to override user-provided defaults programmatically. Alternatively, use session.useConfigData() to replace the entire configuration object before calling run().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →