# How to Use a Custom Main Function with Catch2: Four Implementation Patterns

> Master Catch2 custom main functions. Learn four implementation patterns to control test execution instead of linking Catch2Main. Boost your testing workflow today.

- Repository: [Catch Org/Catch2](https://github.com/catchorg/Catch2)
- Tags: how-to-guide
- Published: 2026-07-30

---

**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`](https://github.com/catchorg/Catch2/blob/main/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`](https://github.com/catchorg/Catch2/blob/main/src/catch2/catch_session.hpp) include:

- `run(int argc, char const* argv[])` - Single-step initialization and execution
- `applyCommandLine(int argc, char const* argv[])` - Parses arguments without running tests
- `configData()` - Returns a mutable reference to the underlying `ConfigData` structure
- `cli()` - Gets or sets the Clara command-line parser instance
- `useConfigData(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.

```cpp
#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`](https://github.com/catchorg/Catch2/blob/main/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()`.

```cpp
#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.

```cpp
#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.

```cpp
#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`](https://github.com/catchorg/Catch2/blob/main/docs/own-main.md) and is particularly useful when integrating Catch2 into applications with constrained execution environments.

## Summary

- Include `<catch2/catch_session.hpp>` (or [`extras/catch_amalgamated.hpp`](https://github.com/catchorg/Catch2/blob/main/extras/catch_amalgamated.hpp) for single-header builds) to access the `Catch::Session` class
- Link against the `Catch2` target instead of `Catch2Main` when providing your own `main()` function
- Use `session.applyCommandLine()` to parse arguments, then modify `session.configData()` for post-parse configuration changes
- Extend the CLI parser by composing `Catch::Clara::Opt` objects with `session.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`](https://github.com/catchorg/Catch2/blob/main/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()`.