{"article":{"slug":"c-26-the-new-debugging-header","title":"C++26: the new debugging header","subtitle":null,"summary":"Sandor Dargo explains C++26's new <debugging> header from P2546R5 and P2810R4, which standardizes what projects like Boost.Test, Catch2, Dear ImGui and Unreal each reinvented: std::breakpoint, std::is_debugger_present and std::breakpoint_if_debugging, replacing platform-specific breakpoint macros.","content_type":"tutorial","language":"en","canonical_url":"https://www.sandordargo.com/blog/2026/09/30/cpp26-debugging","author":{"name":"Sandor Dargo","url":"https://www.sandordargo.com","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Sandor Dargo's Blog","url":"https://www.sandordargo.com","listing_slug":null,"listing":null},"topics":[{"name":"C++","slug":"c","url":"https://listedarticles.com/topics/c"},{"name":"Programming Languages","slug":"programming-languages","url":"https://listedarticles.com/topics/programming-languages"},{"name":"Debugging","slug":"debugging","url":"https://listedarticles.com/topics/debugging"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":559,"reading_minutes":2,"published_at":"2026-09-29T22:00:00.000Z","added_at":"2026-10-16T23:11:32.869Z","updated_at":"2026-10-16T23:11:32.869Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/c-26-the-new-debugging-header","markdown_url":"https://listedarticles.com/articles/c-26-the-new-debugging-header.md","example":false,"citation":"Sandor Dargo, Sandor Dargo's Blog. \"C++26: the new debugging header.\" 29 Sept 2026. https://www.sandordargo.com/blog/2026/09/30/cpp26-debugging (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://www.sandordargo.com/blog/2026/09/30/cpp26-debugging"},"body_markdown":"# C++26: the new debugging header\n\nIf you’ve ever needed to hit a breakpoint programmatically in C++, you’ve probably written something like this:\n\n```cpp\n#if defined(_MSC_VER)\n    __debugbreak();\n#elif defined(__clang__)\n    __builtin_debugtrap();\n#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))\n    __asm__ volatile(\"int3\");\n#else\n    raise(SIGTRAP);\n#endif\n```\n\nEvery major C++ project reinvents this wheel. Boost.Test has `debugger_break()`, Catch2 has `CATCH_BREAK_INTO_DEBUGGER`, Dear ImGui has `IM_DEBUG_BREAK()` and Unreal has `UE_DEBUG_BREAK`. The platform knowledge required is arcane but well-known to tooling implementers — it just wasn’t standardized.\n\nC++26 fixes this with [P2546R5](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2546r5.html) and its companion [P2810R4](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2810r4.html) thanks to Rene Ferdinand Rivera Morell. A new `<debugging>` header provides three functions.\n\n## The three functions\n\n```cpp\n#include <debugging>\n\nnamespace std {\n    void breakpoint() noexcept;\n    bool is_debugger_present() noexcept;\n    void breakpoint_if_debugging() noexcept;\n}\n```\n\nAll three are `noexcept` and all three are freestanding. None are `constexpr` — debugging is a runtime concern.\n\n### `std::breakpoint()`\n\nAn unconditional breakpoint. When a debugger is attached, execution halts and control is handed to the debugger. On x86, this typically compiles to a single `int3` instruction.\n\nWhen no debugger is present, the behaviour is implementation-defined — on most platforms the process receives a signal (such as `SIGTRAP`) and will terminate unless the signal is caught.\n\n### `std::is_debugger_present()`\n\nReturns `true` if the program is currently being traced by a debugger. The check is performed immediately on each call — the result is not cached because a debugger can attach or detach at any time during execution.\n\nHow this works under the hood varies by platform. On Windows, for example, an implementation might call `::IsDebuggerPresent()`. On Linux, GCC’s libstdc++ reads `/proc/self/status` and checks the `TracerPid` field. The standard doesn’t mandate any particular mechanism — the semantics are implementation-defined, with the intended behaviour described in non-normative notes.\n\nWhat makes this function interesting is that it’s **replaceable**, in the same sense as `operator new` and `operator delete`. You can define your own version:\n\n```cpp\nbool std::is_debugger_present() noexcept {\n    return my_hardware_debug_flag;\n}\n```\n\nThis was introduced by [P2810R4](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2810r4.html) to solve the freestanding problem: on bare-metal platforms, there may be no OS-level way to detect a debugger. Without replaceability, the function would either have to be excluded from freestanding (forcing `#ifdef` guards everywhere) or return a useless default. Replaceability lets you wire it to whatever your platform provides.\n\n### `std::breakpoint_if_debugging()`\n\nThe convenience function most users will reach for. It’s equivalent to:\n\n```cpp\nif (std::is_debugger_present())\n    std::breakpoint();\n```\n\nWhen a debugger is attached, you hit a breakpoint. When it’s not, nothing happens. This is the pattern that Catch2, Boost.Test, and every other framework implements manually.\n\n## Beyond breakpoints\n\nHaving `is_debugger_present()` as a separate function enables more than just conditional breakpoints. You might use it to:\n\n- Produce extra diagnostic output when debugging- Disable timeouts that would fire during a stepping session- Switch to a more debugger-friendly allocator- Skip optimizations that make debugging harder\n\nThe separation of concerns is deliberate — `breakpoint()` and `is_debugger_present()` are orthogonal building blocks, and `breakpoint_if_debugging()` is the convenience composition.\n\n## Conclusion\n\n`<debugging>` is another one of those C++26 additions that makes you wonder why it took this long. The platform knowledge is well-understood, every major project already has its own version, and the API is three functions. Sometimes the most useful standard library additions are the simplest ones.\n","body_html":"<h1 id=\"c-26-the-new-debugging-header\">C++26: the new debugging header</h1>\n<p>If you’ve ever needed to hit a breakpoint programmatically in C++, you’ve probably written something like this:</p>\n<pre><code class=\"language-cpp\">#if defined(_MSC_VER)\n    __debugbreak();\n#elif defined(__clang__)\n    __builtin_debugtrap();\n#elif defined(__GNUC__) &amp;&amp; (defined(__i386__) || defined(__x86_64__))\n    __asm__ volatile(&quot;int3&quot;);\n#else\n    raise(SIGTRAP);\n#endif</code></pre>\n<p>Every major C++ project reinvents this wheel. Boost.Test has <code>debugger_break()</code>, Catch2 has <code>CATCH_BREAK_INTO_DEBUGGER</code>, Dear ImGui has <code>IM_DEBUG_BREAK()</code> and Unreal has <code>UE_DEBUG_BREAK</code>. The platform knowledge required is arcane but well-known to tooling implementers — it just wasn’t standardized.</p>\n<p>C++26 fixes this with <a href=\"https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2546r5.html\" rel=\"nofollow ugc noopener\">P2546R5</a> and its companion <a href=\"https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2810r4.html\" rel=\"nofollow ugc noopener\">P2810R4</a> thanks to Rene Ferdinand Rivera Morell. A new <code>&lt;debugging&gt;</code> header provides three functions.</p>\n<h2 id=\"the-three-functions\">The three functions</h2>\n<pre><code class=\"language-cpp\">#include &lt;debugging&gt;\n\nnamespace std {\n    void breakpoint() noexcept;\n    bool is_debugger_present() noexcept;\n    void breakpoint_if_debugging() noexcept;\n}</code></pre>\n<p>All three are <code>noexcept</code> and all three are freestanding. None are <code>constexpr</code> — debugging is a runtime concern.</p>\n<h3 id=\"std-breakpoint\"><code>std::breakpoint()</code></h3>\n<p>An unconditional breakpoint. When a debugger is attached, execution halts and control is handed to the debugger. On x86, this typically compiles to a single <code>int3</code> instruction.</p>\n<p>When no debugger is present, the behaviour is implementation-defined — on most platforms the process receives a signal (such as <code>SIGTRAP</code>) and will terminate unless the signal is caught.</p>\n<h3 id=\"std-is-debugger-present\"><code>std::is_debugger_present()</code></h3>\n<p>Returns <code>true</code> if the program is currently being traced by a debugger. The check is performed immediately on each call — the result is not cached because a debugger can attach or detach at any time during execution.</p>\n<p>How this works under the hood varies by platform. On Windows, for example, an implementation might call <code>::IsDebuggerPresent()</code>. On Linux, GCC’s libstdc++ reads <code>/proc/self/status</code> and checks the <code>TracerPid</code> field. The standard doesn’t mandate any particular mechanism — the semantics are implementation-defined, with the intended behaviour described in non-normative notes.</p>\n<p>What makes this function interesting is that it’s <strong>replaceable</strong>, in the same sense as <code>operator new</code> and <code>operator delete</code>. You can define your own version:</p>\n<pre><code class=\"language-cpp\">bool std::is_debugger_present() noexcept {\n    return my_hardware_debug_flag;\n}</code></pre>\n<p>This was introduced by <a href=\"https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2810r4.html\" rel=\"nofollow ugc noopener\">P2810R4</a> to solve the freestanding problem: on bare-metal platforms, there may be no OS-level way to detect a debugger. Without replaceability, the function would either have to be excluded from freestanding (forcing <code>#ifdef</code> guards everywhere) or return a useless default. Replaceability lets you wire it to whatever your platform provides.</p>\n<h3 id=\"std-breakpoint-if-debugging\"><code>std::breakpoint_if_debugging()</code></h3>\n<p>The convenience function most users will reach for. It’s equivalent to:</p>\n<pre><code class=\"language-cpp\">if (std::is_debugger_present())\n    std::breakpoint();</code></pre>\n<p>When a debugger is attached, you hit a breakpoint. When it’s not, nothing happens. This is the pattern that Catch2, Boost.Test, and every other framework implements manually.</p>\n<h2 id=\"beyond-breakpoints\">Beyond breakpoints</h2>\n<p>Having <code>is_debugger_present()</code> as a separate function enables more than just conditional breakpoints. You might use it to:</p>\n<ul><li>Produce extra diagnostic output when debugging- Disable timeouts that would fire during a stepping session- Switch to a more debugger-friendly allocator- Skip optimizations that make debugging harder</li></ul>\n<p>The separation of concerns is deliberate — <code>breakpoint()</code> and <code>is_debugger_present()</code> are orthogonal building blocks, and <code>breakpoint_if_debugging()</code> is the convenience composition.</p>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p><code>&lt;debugging&gt;</code> is another one of those C++26 additions that makes you wonder why it took this long. The platform knowledge is well-understood, every major project already has its own version, and the API is three functions. Sometimes the most useful standard library additions are the simplest ones.</p>","headings":[{"level":1,"text":"C++26: the new debugging header","id":"c-26-the-new-debugging-header"},{"level":2,"text":"The three functions","id":"the-three-functions"},{"level":3,"text":"std::breakpoint()","id":"std-breakpoint"},{"level":3,"text":"std::is_debugger_present()","id":"std-is-debugger-present"},{"level":3,"text":"std::breakpoint_if_debugging()","id":"std-breakpoint-if-debugging"},{"level":2,"text":"Beyond breakpoints","id":"beyond-breakpoints"},{"level":2,"text":"Conclusion","id":"conclusion"}]}}