{"id":13903264,"url":"https://github.com/jeremy-rifkin/libassert","last_synced_at":"2025-05-15T10:07:04.403Z","repository":{"id":40313294,"uuid":"403681183","full_name":"jeremy-rifkin/libassert","owner":"jeremy-rifkin","description":"The most over-engineered C++ assertion library","archived":false,"fork":false,"pushed_at":"2025-04-11T23:26:58.000Z","size":2232,"stargazers_count":605,"open_issues_count":8,"forks_count":47,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-04-14T16:57:35.505Z","etag":null,"topics":["assertions","cpp","diagnostics"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jeremy-rifkin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-09-06T15:56:20.000Z","updated_at":"2025-04-13T13:30:20.000Z","dependencies_parsed_at":"2023-01-21T04:18:22.492Z","dependency_job_id":"ed59368d-9ed2-4b05-a56d-d4aac4e10185","html_url":"https://github.com/jeremy-rifkin/libassert","commit_stats":{"total_commits":535,"total_committers":15,"mean_commits":"35.666666666666664","dds":0.04299065420560744,"last_synced_commit":"d2902fc5e60f8f59bb67e27ab5d89220ab457885"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremy-rifkin%2Flibassert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremy-rifkin%2Flibassert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremy-rifkin%2Flibassert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremy-rifkin%2Flibassert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeremy-rifkin","download_url":"https://codeload.github.com/jeremy-rifkin/libassert/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254319720,"owners_count":22051073,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assertions","cpp","diagnostics"],"created_at":"2024-08-06T22:01:58.138Z","updated_at":"2025-05-15T10:06:59.390Z","avatar_url":"https://github.com/jeremy-rifkin.png","language":"C++","funding_links":[],"categories":["Debug","cpp"],"sub_categories":[],"readme":"# libassert \u003c!-- omit in toc --\u003e\n\n[![build](https://github.com/jeremy-rifkin/libassert/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/jeremy-rifkin/libassert/actions/workflows/build.yml)\n[![tests](https://github.com/jeremy-rifkin/libassert/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/jeremy-rifkin/libassert/actions/workflows/tests.yml)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jeremy-rifkin_libassert\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=jeremy-rifkin_libassert)\n\u003cbr/\u003e\n[![Community Discord Link](https://img.shields.io/badge/Chat%20on%20the%20(very%20small)-Community%20Discord-blue?labelColor=2C3239\u0026color=7289DA\u0026style=flat\u0026logo=discord\u0026logoColor=959DA5)](https://discord.gg/frjaAZvqUZ)\n\u003c!--\n\u003cbr/\u003e\n[![Try on Compiler Explorer](https://img.shields.io/badge/-Compiler%20Explorer-brightgreen?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAYAAAAmlE46AAAACXBIWXMAAACwAAAAsAEUaqtpAAABSElEQVQokYVTsU7DMBB9QMTCEJbOMLB5oF0tRfUPIPIJZctYJkZYu3WMxNL+ARUfQKpImcPgDYnsXWBgYQl61TkYyxI3Wef37j3fnQ/6vkcsikY9AbiWq0mpbevDBmLRqDEAA4CEHMADgFRwrwDmch6X2i73RCFVHvC/WCeCMAFpC2AFoPPu5x4md4rnAN4luS61nYWSgauNU8ydkr0bLTMYAoIYtWqxM4LtEumeERDtfUjlMDrp7L67iddyyJtOvUIu2rquVn4iiVSOKXYhiMSJWLwUJZLuQ2CWmVldV4MT11UmXgB8fr0dX3WP6VHMiVrscim6Da2mJxffzwSU2v6xWzSKmzQ4cUTOaCBTvWgU14xkzjhckKm/q3wnrRAcAhksxMZNAdxEf0fRKI6E8zqT1C0X28ccRpqAUltW5pu4sxv5Mb8B4AciE3bHMxz/+gAAAABJRU5ErkJggg==\u0026labelColor=2C3239\u0026style=flat\u0026label=Try+it+on\u0026color=30C452)](https://godbolt.org/z/Eonafvxof)\n--\u003e\n\n\u003cp align=\"center\"\u003eThe most over-engineered C++ assertion library\u003c/p\u003e\n\n## Table of Contents: \u003c!-- omit in toc --\u003e\n- [30-Second Overview](#30-second-overview)\n  - [CMake FetchContent Usage](#cmake-fetchcontent-usage)\n- [Philosophy](#philosophy)\n- [Features](#features)\n  - [Custom Failure Handlers](#custom-failure-handlers)\n  - [Smart literal formatting](#smart-literal-formatting)\n  - [Integrations with Catch2 and GoogleTest](#integrations-with-catch2-and-googletest)\n- [Methodology](#methodology)\n- [Considerations](#considerations)\n- [In-Depth Library Documentation](#in-depth-library-documentation)\n  - [Assertion Macros](#assertion-macros)\n    - [Parameters](#parameters)\n    - [Return value](#return-value)\n  - [General Utilities](#general-utilities)\n  - [Terminal Utilities](#terminal-utilities)\n  - [Configuration](#configuration)\n  - [Assertion information](#assertion-information)\n    - [Anatomy of Assertion Information](#anatomy-of-assertion-information)\n  - [Stringification of Custom Objects](#stringification-of-custom-objects)\n  - [Custom Failure Handlers](#custom-failure-handlers-1)\n  - [Breakpoints](#breakpoints)\n  - [Other Configurations](#other-configurations)\n  - [Library Version](#library-version)\n- [Integration with Test Libraries](#integration-with-test-libraries)\n  - [Catch2](#catch2)\n  - [GoogleTest](#googletest)\n- [Usage](#usage)\n  - [CMake FetchContent](#cmake-fetchcontent)\n  - [System-Wide Installation](#system-wide-installation)\n  - [Local User Installation](#local-user-installation)\n  - [Use Without CMake](#use-without-cmake)\n  - [Package Managers](#package-managers)\n    - [Conan](#conan)\n    - [Vcpkg](#vcpkg)\n- [Platform Logistics](#platform-logistics)\n- [Replacing \\\u003ccassert\\\u003e](#replacing-cassert)\n- [FAQ](#faq)\n- [Cool projects using libassert](#cool-projects-using-libassert)\n- [Comparison With Other Languages](#comparison-with-other-languages)\n\n# 30-Second Overview\n\n**Library philosophy:** Provide as much helpful diagnostic info as possible.\n\n**Some of the awesome things the library does:**\n\n```cpp\n#include \u003clibassert/assert.hpp\u003e\nvoid zoog(const std::map\u003cstd::string, int\u003e\u0026 map) {\n    DEBUG_ASSERT(map.contains(\"foo\"), \"expected key not found\", map);\n}\n```\n![](screenshots/map_contains.png)\n\n```cpp\nASSERT(vec.size() \u003e min_items(), \"vector doesn't have enough items\", vec);\n```\n![](screenshots/vec_size.png)\n\n```cpp\nstd::optional\u003cfloat\u003e get_param();\nfloat f = *ASSERT_VAL(get_param());\n```\n![](screenshots/assert_val_opt.png)\n\n**Types of assertions:**\n\nConditional assertions:\n\n- `DEBUG_ASSERT`: Checked in debug but does nothing in release (analogous to the standard library's `assert`)\n- `ASSERT`: Checked in both debug and release\n- `ASSUME`: Checked in debug and serves as an optimization hint in release\n\nUnconditional assertions:\n- `PANIC`: Triggers in both debug and release\n- `UNREACHABLE`: Triggers in debug, marked as unreachable in release\n\n**Prefer lowecase `assert`?**\n\nYou can enable the lowercase `debug_assert` and `assert` aliases with `-DLIBASSERT_LOWERCASE`.\n\n**Summary of features:**\n\n- Automatic decomposition of assertion expressions without macros such as `ASSERT_EQ` etc.\n- Assertion messages\n- Arbitrary extra diagnostics\n- Syntax highlighting\n- Stack traces\n- `DEBUG_ASSERT_VAL` and `ASSERT_VAL` variants that return a value so they can be integrated seamlessly into code, e.g.\n  `FILE* f = ASSERT_VAL(fopen(path, \"r\") != nullptr)`\n- Smart literal formatting\n- Stringification of user-defined types\n- Custom failure handlers\n- Catch2/Gtest integrations\n- {fmt} support\n- Programatic breakpoints on assertion failures for more debugger-friendly assertions, more info [below](#breakpoints)\n\n## CMake FetchContent Usage\n\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(\n  libassert\n  GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert.git\n  GIT_TAG        v2.1.5 # \u003cHASH or TAG\u003e\n)\nFetchContent_MakeAvailable(libassert)\ntarget_link_libraries(your_target libassert::assert)\n\n# On windows copy libassert.dll to the same directory as the executable for your_target\nif(WIN32)\n  add_custom_command(\n    TARGET your_target POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy_if_different\n    $\u003cTARGET_FILE:libassert::assert\u003e\n    $\u003cTARGET_FILE_DIR:your_target\u003e\n  )\nendif()\n```\n\nBe sure to configure with `-DCMAKE_BUILD_TYPE=Debug` or `-DDCMAKE_BUILD_TYPE=RelWithDebInfo` for symbols and line\ninformation.\n\nOn macOS it is recommended to generate a .dSYM file, see [Platform Logistics](#platform-logistics) below.\n\nFor other ways to use the library, such as through package managers or a system-wide installation, see [Usage](#usage)\nbelow.\n\n# Philosophy\n\nFundamentally the role of assertions is to verify assumptions made in software and identify violations close to their\nsources. Assertion tooling should prioritize providing as much information and context to the developer as possible to\nallow for speedy triage. Unfortunately, existing language and library tooling provides very limited triage information.\n\nFor example with stdlib assertions an assertion such as `assert(n \u003c= 12);` provides no information upon failure about\nwhy it failed or what led to its failure. Providing a stack trace and the value of `n` greatly improves triage and\ndebugging. Ideally an assertion failure should provide enough diagnostic information that the programmmer doesn't have\nto rerun in a debugger to pinpoint the problem.\n\nVersion 1 of this library was an exploration looking at how much helpful information and functionality could be packed\ninto assertions while also providing a quick and easy interface for the developer.\n\nVersion 2 of this library takes lessons learned from version 1 to create a tool that I personally have found\nindispensable in development.\n\n# Features\n\n## Automatic Expression Decomposition \u003c!-- omit in toc --\u003e\nThe most important feature this library supports is automatic expression decomposition. No need for `ASSERT_LT` or other\nsuch hassle, `assert(vec.size() \u003e 10);` is automatically understood, as showcased above.\n\n## Expression Diagnostics \u003c!-- omit in toc --\u003e\nValues involved in assert expressions are displayed. Redundant diagnostics like `2 =\u003e 2` are avoided.\n\n```cpp\nDEBUG_ASSERT(map.count(1) == 2);\n```\n\n![](screenshots/no_redundancy.png)\n\nOnly the full assert expression is able to be extracted from a macro call. Showing which parts of the expression\ncorrespond to what values requires some basic expression parsing. C++ grammar is ambiguous but most expressions can be\ndisambiguated.\n\n## Extra Diagnostics \u003c!-- omit in toc --\u003e\n\nAll assertions in this library support optional diagnostic messages as well as arbitrary other diagnostic messages.\n\n```cpp\nFILE* f = ASSERT_VAL(fopen(path, \"r\") != nullptr, \"Internal error with foobars\", errno, path);\n```\n\nSpecial handling is provided for `errno`, and strerror is automatically called.\n\nNote: Extra diagnostics are only evaluated in the failure path of an assertion.\n\n![](screenshots/fopen.png)\n\n## Stack Traces \u003c!-- omit in toc --\u003e\n\nA lot of work has been put into generating pretty stack traces and formatting them as nicely as possible.\n[Cpptrace](https://github.com/jeremy-rifkin/cpptrace) is used as a portable and self-contained solution for stacktraces\npre-C++23. Optional configurations can be found in the library's documentation.\n\nOne feature worth noting is that instead of always printing full paths, only the minimum number of directories needed to\ndifferentiate paths are printed.\n\n![](screenshots/wubble_trace.png)\n\nAnother feature worth pointing out is that the stack traces will fold traces with deep recursion:\n\n![](screenshots/recursion_fold.png)\n\n## Syntax Highlighting \u003c!-- omit in toc --\u003e\n\nThe assertion handler applies syntax highlighting wherever appropriate, as seen in all the\nscreenshots above. This is to help enhance readability.\n\n## Custom Failure Handlers\n\nLibassert supports custom assertion failure handlers:\n\n```cpp\nvoid handler(const assertion_info\u0026 info) {\n    throw std::runtime_error(\"Assertion failed:\\n\" + assertion.to_string());\n}\n\nint main() {\n    libassert::set_failure_handler(handler);\n}\n```\n\nMore details [below](#custom-failure-handlers-1).\n\n## Debug Stringification \u003c!-- omit in toc --\u003e\n\nA lot of care is given to producing debug stringifications of values as effectively as possible: Strings, characters,\nnumbers, should all be printed as you'd expect. Additionally containers, tuples, std::optional, smart pointers, etc. are\nall stringified to show as much information as possible. If a user defined type overloads `operator\u003c\u003c(std::ostream\u0026 o,\nconst S\u0026 s)`, that overload will be called. Otherwise it a default message will be printed. Additionally, a\nstringification customiztaion point is provided:\n\n```cpp\ntemplate\u003c\u003e struct libassert::stringifier\u003cMyObject\u003e {\n    std::string stringify(const MyObject\u0026 type) {\n        return ...;\n    }\n};\n```\n\n![](screenshots/object_printing.png)\n\n![](screenshots/custom_object_printing.png)\n\n## Smart literal formatting\n\nAssertion values are printed in hex or binary as well as decimal if hex/binary are used on either\nside of an assertion expression:\n\n```cpp\nASSERT(get_mask() == 0b00001101);\n```\n\n![](screenshots/literal_formatting.png)\n\n## Safe Comparisons \u003c!-- omit in toc --\u003e\n\nBecause expressions are already being automatically decomposed, you can opt into having signed-unsigned comparisons done\nautomatically done with sign safety with `-DLIBASSERT_SAFE_COMPARISONS`:\n\n```cpp\nASSERT(18446744073709551606ULL == -10);\n```\n\n![](screenshots/safe_comparison.png)\n\n\n## Integrations with Catch2 and GoogleTest\n\nLibassert provides two headers `\u003clibassert/assert-catch2.hpp\u003e` and `\u003clibassert/assert-gtest.hpp\u003e` for use with catch2\nand GoogleTest.\n\nExample output from gtest:\n\n![](screenshots/gtest.png)\n\nMore information [below](#integration-with-test-libraries).\n\n# Methodology\n\nLibassert provides three types of assertions, each varying slightly depending on when it should be checked and how it\nshould be interpreted:\n\n| Name           | Effect                                                                               |\n| -------------- | ------------------------------------------------------------------------------------ |\n| `DEBUG_ASSERT` | Checked in debug, no codegen in release                                              |\n| `ASSERT`       | Checked in both debug and release builds                                             |\n| `ASSUME`       | Checked in debug, `if(!(expr)) { __builtin_unreachable(); }` in release              |\n\nUnconditional assertions\n| Name          | Effect                                                |\n| ------------- | ----------------------------------------------------- |\n| `PANIC`       | Triggers in both debug and release                    |\n| `UNREACHABLE` | Triggered in debug, marked as unreachable in release. |\n\nOne benefit to `PANIC` and `UNREACHABLE` over `ASSERT(false, ...)` is that the compiler gets `[[noreturn]]` information.\n\n`ASSUME` marks the fail path as unreachable in release, potentially providing helpful information to the optimizer. This\nisn't the default behavior for all assertions because the immediate consequence of this is that assertion failure in\n`-DNDEBUG` can lead to UB and it's better to make this very explicit.\n\nAssertion variants that can be used in-line in an expression, such as\n`FILE* file = ASSERT_VAL(fopen(path, \"r\"), \"Failed to open file\");`, are also available:\n\n| Name               | Effect                                                                  |\n| ------------------ | ----------------------------------------------------------------------- |\n| `DEBUG_ASSERT_VAL` | Checked in debug, must be evaluated in both debug and release           |\n| `ASSERT_VAl`       | Checked in both debug and release builds                                |\n| `ASSUME_VAL`       | Checked in debug, `if(!(expr)) { __builtin_unreachable(); }` in release |\n\nNote: Even in release builds the expression for `DEBUG_ASSERT_VAL` must still be evaluated, unlike `DEBUG_ASSERT`. Of\ncourse, if the result is unused and produces no side effects it will be optimized away.\n\n# Considerations\n\n**Performance:** As far as runtime performance goes, the impact at callsites is very minimal under `-Og` or higher. The fast-path in the\ncode (i.e., where the assertion does not fail), will be fast. A lot of work is required to process assertion failures\nonce they happen. However, since failures should be rare, this should not matter.\n\n**Compile speeds:**, there is a compile-time cost associated with all the template instantiations required for this library's\nmagic.\n\n**Other:**\n\n\u003e [!NOTE]\n\u003e Because of expression decomposition, `ASSERT(1 = 2);` compiles.\n\n# In-Depth Library Documentation\n\n## Library headers\n\n- `libassert/assert.hpp`: The main library header\n- `libassert/assert-gtest.hpp`: Libassert macros for gtest\n- `libassert/assert-catch2.hpp`: Libassert macros for catch2\n\nAdditionally, the `libassert/` include folder contains `expression-decomposition.hpp`, `platform.hpp`,\n`stringification.hpp`, and `utilities.hpp`. These are mostly library details.\n\n## Assertion Macros\n\nAll assertion functions are macros. Here are some pseudo-declarations for interfacing with them:\n\n```cpp\nvoid DEBUG_ASSERT              (expression, [optional message], [optional extra diagnostics, ...]);\nvoid ASSERT                    (expression, [optional message], [optional extra diagnostics, ...]);\nvoid ASSUME                    (expression, [optional message], [optional extra diagnostics, ...]);\ndecltype(auto) DEBUG_ASSERT_VAL(expression, [optional message], [optional extra diagnostics, ...]);\ndecltype(auto) ASSERT_VAL      (expression, [optional message], [optional extra diagnostics, ...]);\ndecltype(auto) ASSUME_VAL      (expression, [optional message], [optional extra diagnostics, ...]);\n\nvoid PANIC      ([optional message], [optional extra diagnostics, ...]);\nvoid UNREACHABLE([optional message], [optional extra diagnostics, ...]);\n```\n\n`-DLIBASSERT_PREFIX_ASSERTIONS` can be used to prefix these macros with `LIBASSERT_`. This is useful for wrapping\nlibassert assertions.\n\n`-DLIBASSERT_LOWERCASE` can be used to enable the `debug_assert` and `assert` aliases for `DEBUG_ASSERT` and `ASSERT`.\nSee: [Replacing \u0026lt;cassert\u0026gt;](#replacing-cassert).\n\n### Parameters\n\n#### `expression` \u003c!-- omit in toc --\u003e\n\nThe `expression` is automatically decomposed so diagnostic information can be provided. The resultant type must be\nconvertible to boolean.\n\nThe operation between left and right hand sides of the top-level operation in the expression tree is evaluated by a\nfunction object.\n\nNote: Boolean logical operators (`\u0026\u0026` and `||`) are not decomposed by default due to short circuiting.\n\n#### `assertion message` \u003c!-- omit in toc --\u003e\n\nAn optional assertion message may be provided. If the first argument following the assertion expression, or the first\nargument in PANIC/UNREACHABLE, is any string type it will be used as the message (if you want the first parameter, which\nhappens to be a string, to be an extra diagnostic value instead simply pass an empty string first, i.e.\n`ASSERT(foo, \"\", str);`).\n\nNote: The assertion message expression is only evaluated in the failure path of the assertion.\n\n#### `extra diagnostics` \u003c!-- omit in toc --\u003e\n\nAn arbitrary number of extra diagnostic values may be provided. These are displayed below the expression diagnostics if\na check fails.\n\nNote: Extra diagnostics are only evaluated in the failure path of the assertion.\n\nThere is special handling when `errno` is provided: The value of `strerror` is displayed automatically.\n\n### Return value\n\nTo facilitate ease of integration into code `_VAL` variants are provided which return a value from the assert\nexpression. The returned value is determined as follows:\n\n- If there is no top-level binary operation (e.g. as in `ASSERT_VAL(foo());` or `ASSERT_VAL(false);`) in the assertion\n  expression, the value of the expression is simply returned.\n- Otherwise if the top-level binary operation is `==`, `!=`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`, `\u0026\u0026`, `||`, or or any assignment or\n  compound assignment then the value of the __left-hand operand__ is returned.\n- Otherwise if the top-level binary operation is `\u0026`, `|`, `^`, `\u003c\u003c`, `\u003e\u003e`, or any binary operator with precedence above\n  bitshift then value of the whole expression is returned.\n\nI.e., `ASSERT_VAL(foo() \u003e 2);` returns the computed result from `foo()` and `ASSERT_VAL(x \u0026 y);` returns the computed\nresult of `x \u0026 y`;\n\nIf the value from the assertion expression selected to be returned is an lvalue, the type of the assertion call will be\nan lvalue reference. If the value from the assertion expression is an rvalue then the type of the call will be an\nrvalue.\n\n## General Utilities\n\n```cpp\nnamespace libassert {\n    [[nodiscard]] std::string stacktrace(\n        int width = 0,\n        const color_scheme\u0026 scheme = get_color_scheme(),\n        std::size_t skip = 0\n    );\n    template\u003ctypename T\u003e [[nodiscard]] std::string_view type_name() noexcept;\n    template\u003ctypename T\u003e [[nodiscard]] std::string pretty_type_name() noexcept;\n    template\u003ctypename T\u003e [[nodiscard]] std::string stringify(const T\u0026 value);\n    std::string highlight(\n        std::string_view expression,\n        const color_scheme\u0026 scheme = get_color_scheme()\n    );\n    template\u003ctypename T\u003e\n    [[nodiscard]] std::string highlight_stringify(\n        const T\u0026 value,\n        const color_scheme\u0026 scheme = get_color_scheme()\n    );\n}\n```\n\n- `stacktrace`: Generates a stack trace, formats to the given width (0 for no width formatting)\n- `type_name`: Returns the type name of T\n- `pretty_type_name`: Returns the prettified type name for T\n- `stringify`: Produces a debug stringification of a value\n- `highlight`: Syntax-highlights a string using libassert's internal expression highlighter\n- `highlight_stringify`: Equivalent to `libassert::highlight(libassert::stringify(value))`\n\n## Terminal Utilities\n\n```cpp\nnamespace libassert {\n    void enable_virtual_terminal_processing_if_needed();\n    inline constexpr int stdin_fileno = 0;\n    inline constexpr int stdout_fileno = 1;\n    inline constexpr int stderr_fileno = 2;\n    bool isatty(int fd);\n    [[nodiscard]] int terminal_width(int fd);\n}\n```\n\n- `enable_virtual_terminal_processing_if_needed`: Enable ANSI escape sequences for terminals on windows, needed for\n  color output.\n- `isatty`: Returns true if the file descriptor corresponds to a terminal\n- `terminal_width`: Returns the width of the terminal represented by fd or 0 on error\n\n## Configuration\n\n### Color Scheme: \u003c!-- omit in toc --\u003e\n\n```cpp\nnamespace libassert {\n    // NOTE: string view underlying data should have static storage duration, or otherwise live as\n    // long as the scheme is in use\n    struct color_scheme {\n        std::string_view string, escape, keyword, named_literal, number, punctuation, operator_token,\n                    call_identifier, scope_resolution_identifier, identifier, accent, unknown, reset;\n        static const color_scheme ansi_basic;\n        static const color_scheme ansi_rgb;\n        static const color_scheme blank;\n    };\n    void set_color_scheme(const color_scheme\u0026);\n    const color_scheme\u0026 get_color_scheme();\n}\n```\n\nBy default `color_scheme::ansi_rgb` is used. To disable colors, use `color_scheme::blank`.\n\n- `set_color_scheme`: Sets the color scheme for the default assertion handler when stderr is a terminal\n\n### Separator: \u003c!-- omit in toc --\u003e\n\n```cpp\nnamespace libassert {\n    void set_separator(std::string_view separator);\n}\n```\n\n- `set_separator`: Sets the separator between expression and value in assertion diagnostic output. Default: `=\u003e`. NOTE:\n  Not thread-safe.\n\n### Literal formatting mode: \u003c!-- omit in toc --\u003e\n\n```cpp\nnamespace libassert {\n    enum class literal_format_mode {\n        infer, // infer literal formats based on the assertion condition\n        no_variations, // don't do any literal format variations, just default\n        fixed_variations // always use a fixed set of formats (in addition to the default format)\n    };\n    void set_literal_format_mode(literal_format_mode);\n\n    enum class literal_format : unsigned {\n        // integers and floats are decimal by default, chars are of course chars, and everything\n        // else only has one format that makes sense\n        default_format = 0,\n        integer_hex = 1,\n        integer_octal = 2,\n        integer_binary = 4,\n        integer_character = 8, // format integers as characters and characters as integers\n        float_hex = 16,\n    };\n    [[nodiscard]] constexpr literal_format operator|(literal_format a, literal_format b);\n    void set_fixed_literal_format(literal_format);\n}\n```\n\n- `set_literal_format_mode`: Sets whether the library should show literal variations or infer them\n- `set_fixed_literal_format`: Set a fixed literal format configuration, automatically changes the literal_format_mode;\n  note that the default format will always be used along with others\n\n### Path mode: \u003c!-- omit in toc --\u003e\n\n```cpp\nnamespace libassert {\n    enum class path_mode {\n        full, // full path is used\n        disambiguated, // only enough folders needed to disambiguate are provided\n        basename, // only the file name is used\n    };\n    LIBASSERT_EXPORT void set_path_mode(path_mode mode);\n}\n```\n\n- `set_path_mode`: Sets the path shortening mode for assertion output. Default: `path_mode::disambiguated`.\n\n## Assertion information\n\n```cpp\nnamespace libassert {\n    enum class assert_type {\n        debug_assertion,\n        assertion,\n        assumption,\n        panic,\n        unreachable\n    };\n\n    struct LIBASSERT_EXPORT binary_diagnostics_descriptor {\n        std::string left_expression;\n        std::string right_expression;\n        std::string left_stringification;\n        std::string right_stringification;\n    };\n\n    struct extra_diagnostic {\n        std::string_view expression;\n        std::string stringification;\n    };\n\n    struct LIBASSERT_EXPORT assertion_info {\n        std::string_view macro_name;\n        assert_type type;\n        std::string_view expression_string;\n        std::string_view file_name;\n        std::uint32_t line;\n        std::string_view function;\n        std::optional\u003cstd::string\u003e message;\n        std::optional\u003cbinary_diagnostics_descriptor\u003e binary_diagnostics;\n        std::vector\u003cextra_diagnostic\u003e extra_diagnostics;\n        size_t n_args;\n\n        std::string_view action() const;\n\n        const cpptrace::raw_trace\u0026 get_raw_trace() const;\n        const cpptrace::stacktrace\u0026 get_stacktrace() const;\n\n        [[nodiscard]] std::string header(int width = 0, const color_scheme\u0026 scheme = get_color_scheme()) const;\n        [[nodiscard]] std::string tagline(const color_scheme\u0026 scheme = get_color_scheme()) const;\n        [[nodiscard]] std::string location() const;\n        [[nodiscard]] std::string statement(const color_scheme\u0026 scheme = get_color_scheme()) const;\n        [[nodiscard]] std::string print_binary_diagnostics(int width = 0, const color_scheme\u0026 scheme = get_color_scheme()) const;\n        [[nodiscard]] std::string print_extra_diagnostics(int width = 0, const color_scheme\u0026 scheme = get_color_scheme()) const;\n        [[nodiscard]] std::string print_stacktrace(int width = 0, const color_scheme\u0026 scheme = get_color_scheme()) const;\n\n        [[nodiscard]] std::string to_string(int width = 0, const color_scheme\u0026 scheme = get_color_scheme()) const;\n    };\n}\n```\n\n### Anatomy of Assertion Information\n\n```\nDebug Assertion failed at demo.cpp:194: void foo::baz(): Internal error with foobars\n    debug_assert(open(path, 0) \u003e= 0, ...);\n    Where:\n        open(path, 0) =\u003e -1\n    Extra diagnostics:\n        errno =\u003e  2 \"No such file or directory\"\n        path  =\u003e \"/home/foobar/baz\"\n\nStack trace:\n#1 demo.cpp:194 foo::baz()\n#2 demo.cpp:172 void foo::bar\u003cint\u003e(std::pair\u003cint, int\u003e)\n#3 demo.cpp:396 main\n```\n\n- `Debug Assertion failed`: `assertion_info.action()`\n- `demo.cpp:194`: `assertion_info.file_name` and `assertion_info.line`\n- `void foo::baz()`: `assertion_info.pretty_function`\n- `Internal error with foobars`: `assertion_info.message`\n- `debug_assert`: `assertion_info.macro_name`\n- `open(path, 0) \u003e= 0`: `assertion_info.expression_string`\n- `...`: determined by `assertion_info.n_args` which has the total number of arguments passed to the assertion macro\n- Where clause\n  - `open(path, 0)`: `assertion_info.binary_diagnostics.left_expression`\n  - `-1`: `assertion_info.binary_diagnostics.left_stringification`\n  - Same for the right side (omitted in this case because `0 =\u003e 0` isn't useful)\n- Extra diagnostics\n  - `errno`: `assertion_info.extra_diagnostics[0].expression`\n  - `2 \"No such file or directory\"`: `assertion_info.extra_diagnostics[0].stringification`\n  - ... etc.\n- Stack trace\n  - `assertion_info.get_stacktrace()`, or `assertion_info.get_raw_trace()` to get the trace without resolving it\n\n**Helpers:**\n\n`assertion_info.header()`:\n\n```\nDebug Assertion failed at demo.cpp:194: void foo::baz(): Internal error with foobars\n    debug_assert(open(path, 0) \u003e= 0, ...);\n    Where:\n        open(path, 0) =\u003e -1\n    Extra diagnostics:\n        errno =\u003e  2 \"No such file or directory\"\n        path  =\u003e \"/home/foobar/baz\"\n```\n\n`assertion_info.tagline()`:\n\n```\nDebug Assertion failed at demo.cpp:194: void foo::baz(): Internal error with foobars\n```\n\n`assertion_info.location()`:\n\n\u003e [!NOTE]\n\u003e Path processing will be performed according to the path mode\n\n```\ndemo.cpp:194\n```\n\n`assertion_info.statement()`:\n\n```\n    debug_assert(open(path, 0) \u003e= 0, ...);\n```\n\n`assertion_info.print_binary_diagnostics()`:\n\n```\n    Where:\n        open(path, 0) =\u003e -1\n```\n\n`assertion_info.print_extra_diagnostics()`:\n\n```\n    Extra diagnostics:\n        errno =\u003e  2 \"No such file or directory\"\n        path  =\u003e \"/home/foobar/baz\"\n```\n\n`assertion_info.print_stacktrace()`:\n\n```\nStack trace:\n#1 demo.cpp:194 foo::baz()\n#2 demo.cpp:172 void foo::bar\u003cint\u003e(std::pair\u003cint, int\u003e)\n#3 demo.cpp:396 main\n```\n\n## Stringification of Custom Objects\n\nLibassert provides a customization point for user-defined types:\n\n```cpp\ntemplate\u003c\u003e struct libassert::stringifier\u003cMyObject\u003e {\n    std::string stringify(const MyObject\u0026 type) {\n        return ...;\n    }\n};\n```\n\nBy default any container-like user-defined types will be automatically stringifiable.\n\nAdditionally, `LIBASSERT_USE_FMT` can be used to allow libassert to use `fmt::formatter`s.\n\nLastly, any types with an ostream `operator\u003c\u003c` overload can be stringified.\n\n## Custom Failure Handlers\n\n```cpp\nnamespace libassert {\n    void set_failure_handler(void (*handler)(const assertion_info\u0026));\n    [[noreturn]] void default_failure_handler(const assertion_info\u0026 info);\n}\n```\n\n- `set_failure_handler`: Sets the assertion handler for the program.\n- `default_failure_handler`: The default failure handler, provided for convenience.\n\nExample: If you wanted to log to a file in addition to the default behavior you could do something along the lines of:\n\n```cpp\nvoid handler(const assertion_info\u0026 info) {\n    logger::error(info.to_string());\n    libassert::default_failure_handler(info);\n}\n```\n\nFor more complex custom handling you can modify the default handler's logic:\n\n```cpp\nvoid default_failure_handler(const assertion_info\u0026 info) {\n    libassert::enable_virtual_terminal_processing_if_needed(); // for terminal colors on windows\n    std::string message = info.to_string(\n        libassert::terminal_width(libassert::stderr_fileno),\n        libassert::isatty(libassert::stderr_fileno)\n            ? libassert::get_color_scheme()\n            : libassert::color_scheme::blank\n    );\n    std::cerr \u003c\u003c message \u003c\u003c std::endl;\n    switch(info.type) {\n        case libassert::assert_type::assertion:\n        case libassert::assert_type::debug_assertion:\n        case libassert::assert_type::assumption:\n        case libassert::assert_type::panic:\n        case libassert::assert_type::unreachable:\n            (void)fflush(stderr);\n            std::abort();\n            // Breaking here as debug CRT allows aborts to be ignored, if someone wants to make a\n            // debug build of this library\n            break;\n        default:\n            std::cerr \u003c\u003c \"Critical error: Unknown libassert::assert_type\" \u003c\u003c std::endl;\n            std::abort(1);\n    }\n}\n```\n\nBy default libassert aborts from all assertion types. However, it may be desirable to throw an exception from some or\nall assertion types instead of aborting.\n\n\u003e [!IMPORTANT]\n\u003e Failure handlers must not return for `assert_type::panic` and `assert_type::unreachable`.\n\n## Breakpoints\n\nLibassert supports programatic breakpoints on assertion failure to make assertions more debugger-friendly by breaking on\nthe assertion line as opposed to several layers deep in a callstack:\n\n![breakpoints](./screenshots/breakpoint.png)\n\nThis functionality is currently opt-in and it can be enabled by defining `LIBASSERT_BREAK_ON_FAIL`. This is best done as\na compiler flag: `-DLIBASSERT_BREAK_ON_FAIL` or `/DLIBASSERT_BREAK_ON_FAIL`.\n\nInternally the library checks for the presense of a debugger before executing an instruction to breakpoint the debugger.\nBy default the check is only performed once on the first assertion failure. In some scenarios it may be desirable to\nconfigure this check to always be performed, e.g. if you're using a custom assertion handler that throws an exception\ninstead of aborting and you may be able to recover from an assertion failure allowing additional failures later and you\nonly attach a debugger part-way through the run of your program. You can use `libassert::set_debugger_check_mode` to\ncontrol how this check is performed:\n\n```cpp\nnamespace libassert {\n    enum class debugger_check_mode {\n        check_once,\n        check_every_time,\n    };\n    void set_debugger_check_mode(debugger_check_mode mode) noexcept;\n}\n```\n\nThe library also exposes its internal utilities for setting breakpoints and checking if the program is being debugged:\n\n```cpp\nnamespace libassert {\n    bool is_debugger_present() noexcept;\n}\n#define LIBASSERT_BREAKPOINT() \u003c...internals...\u003e\n#define LIBASSERT_BREAKPOINT_IF_DEBUGGING() \u003c...internals...\u003e\n```\n\nThis API mimics the API of [P2514](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2546r3.html), which has\nbeen accepted to C++26.\n\nA note about `constexpr`: For clang and msvc libassert can use compiler intrinsics, however, for gcc inline assembly is\nrequired. Inline assembly isn't allowed in constexpr functions pre-C++20, however, gcc supports it with a warning after\ngcc 10 and the library can surpress that warning for gcc 12. \u003c!-- https://godbolt.org/z/ETjePhT3v --\u003e\n\n## Other Configurations\n\n**Defines:**\n\n- `LIBASSERT_USE_MAGIC_ENUM`: Use magic enum for stringifying enum values\n- `LIBASSERT_DECOMPOSE_BINARY_LOGICAL`: Decompose `\u0026\u0026` and `||`\n- `LIBASSERT_SAFE_COMPARISONS`: Enable safe signed-unsigned comparisons for decomposed expressions\n- `LIBASSERT_PREFIX_ASSERTIONS`: Prefixes all assertion macros with `LIBASSERT_`\n- `LIBASSERT_USE_FMT`: Enables libfmt integration\n- `LIBASSERT_NO_STRINGIFY_SMART_POINTER_OBJECTS`: Disables stringification of smart pointer contents\n\n**CMake:**\n- `LIBASSERT_USE_EXTERNAL_CPPTRACE`: Use an externam cpptrace instead of aquiring the library with FetchContent\n- `LIBASSERT_USE_EXTERNAL_MAGIC_ENUM`: Use an externam magic enum instead of aquiring the library with FetchContent\n\n## Library Version\n\n`\u003clibassert/version.hpp\u003e` provides version macros for the library.\n\n# Integration with Test Libraries\n\n\u003e [!NOTE]\n\u003e Because of MSVC's non-conformant preprocessor there is no easy way to provide assertion wrappers. In order to use test\n\u003e library integrations `/Zc:preprocessor` is required.\n\n## Catch2\n\nLibassert provides a catch2 integration in `libassert/assert-catch2.hpp`:\n\n```cpp\n#include \u003clibassert/assert-catch2.hpp\u003e\n\nTEST_CASE(\"1 + 1 is 2\") {\n    ASSERT(1 + 1 == 3);\n}\n```\n\n![](screenshots/catch2.png)\n\nCurrently the only macro provided is `ASSERT`, which will perform a `REQUIRE` internally.\n\nNote: Before v3.6.0 ansi color codes interfere with Catch2's line wrapping so color is disabled on older versions.\n\n## GoogleTest\n\nLibassert provides a gtest integration in `libassert/assert-gtest.hpp`:\n\n```cpp\n#include \u003clibassert/assert-gtest.hpp\u003e\n\nTEST(Addition, Arithmetic) {\n    ASSERT(1 + 1 == 3);\n}\n```\n\n![](screenshots/gtest.png)\n\nCurrently libassert provides `ASSERT` and `EXPECT` macros for gtest.\n\nThis isn't as pretty as I would like, however, it gets the job done.\n\n# Usage\n\nThis library targets \u003e=C++17 and supports all major compilers and all major platforms (linux, macos, windows, and\nmingw).\n\nNote: The library does rely on some compiler extensions and compiler specific features so it is not compatible with\n`-pedantic`.\n\n## CMake FetchContent\n\nWith CMake FetchContent:\n\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(\n  libassert\n  GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert.git\n  GIT_TAG        v2.1.5 # \u003cHASH or TAG\u003e\n)\nFetchContent_MakeAvailable(libassert)\ntarget_link_libraries(your_target libassert::assert)\n```\n\nNote: On windows and macos some extra work is recommended, see [Platform Logistics](#platform-logistics) below.\n\nBe sure to configure with `-DCMAKE_BUILD_TYPE=Debug` or `-DDCMAKE_BUILD_TYPE=RelWithDebInfo` for symbols and line\ninformation.\n\n## System-Wide Installation\n\n```sh\ngit clone https://github.com/jeremy-rifkin/libassert.git\ngit checkout v2.1.5\nmkdir libassert/build\ncd libassert/build\ncmake .. -DCMAKE_BUILD_TYPE=Release\nmake -j\nsudo make install\n```\n\nUsing through cmake:\n```cmake\nfind_package(libassert REQUIRED)\ntarget_link_libraries(\u003cyour target\u003e libassert::assert)\n```\nBe sure to configure with `-DCMAKE_BUILD_TYPE=Debug` or `-DDCMAKE_BUILD_TYPE=RelWithDebInfo` for symbols and line\ninformation.\n\nOr compile with `-lassert`:\n\n```sh\ng++ main.cpp -o main -g -Wall -lassert\n./main\n```\n\nIf you get an error along the lines of\n```\nerror while loading shared libraries: libassert.so: cannot open shared object file: No such file or directory\n```\nYou may have to run `sudo /sbin/ldconfig` to create any necessary links and update caches so the system can find\nlibcpptrace.so (I had to do this on Ubuntu). Only when installing system-wide. Usually your package manger does this for\nyou when installing new libraries.\n\n\u003cdetails\u003e\n    \u003csummary\u003eSystem-wide install on windows\u003c/summary\u003e\n\n```ps1\ngit clone https://github.com/jeremy-rifkin/libassert.git\ngit checkout v2.1.5\nmkdir libassert/build\ncd libassert/build\ncmake .. -DCMAKE_BUILD_TYPE=Release\nmsbuild libassert.sln\nmsbuild INSTALL.vcxproj\n```\n\nNote: You'll need to run as an administrator in a developer powershell, or use vcvarsall.bat distributed with visual\nstudio to get the correct environment variables set.\n\u003c/details\u003e\n\n## Local User Installation\n\nTo install just for the local user (or any custom prefix):\n\n```sh\ngit clone https://github.com/jeremy-rifkin/libassert.git\ngit checkout v2.1.5\nmkdir libassert/build\ncd libassert/build\ncmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/wherever\nmake -j\nsudo make install\n```\n\nUsing through cmake:\n```cmake\nfind_package(libassert REQUIRED PATHS $ENV{HOME}/wherever)\ntarget_link_libraries(\u003cyour target\u003e libassert::assert)\n```\n\nUsing manually:\n```\ng++ main.cpp -o main -g -Wall -I$HOME/wherever/include -L$HOME/wherever/lib -lassert\n```\n\n## Use Without CMake\n\nTo use the library without cmake first follow the installation instructions at\n[System-Wide Installation](#system-wide-installation), [Local User Installation](#local-user-installation),\nor [Package Managers](#package-managers).\n\nUse the following arguments to compile with libassert:\n\n| Compiler                | Platform         | Dependencies                                              |\n| ----------------------- | ---------------- | --------------------------------------------------------- |\n| gcc, clang, intel, etc. | Linux/macos/unix | `-libassert -I[path] [cpptrace args]` |\n| mingw                   | Windows          | `-libassert -I[path] [cpptrace args]` |\n| msvc                    | Windows          | `assert.lib /I[path] [cpptrace args]` |\n| clang                   | Windows          | `-libassert -I[path] [cpptrace args]` |\n\nFor the `[path]` placeholder in `-I[path]` and `/I[path]`, specify the path to the include folder containing\n`libassert/assert.hpp`.\n\nIf you are linking statically, you will additionally need to specify `-DLIBASSERT_STATIC_DEFINE`.\n\nFor the `[cpptrace args]` placeholder refer to the [cpptrace documentation](https://github.com/jeremy-rifkin/cpptrace?tab=readme-ov-file#use-without-cmake).\n\n## Package Managers\n\n### Conan\n\nLibassert is available through conan at https://conan.io/center/recipes/libassert.\n\n```\n[requires]\nlibassert/2.1.5\n[generators]\nCMakeDeps\nCMakeToolchain\n[layout]\ncmake_layout\n```\n```cmake\n# ...\nfind_package(libassert REQUIRED)\n# ...\ntarget_link_libraries(YOUR_TARGET libassert::assert)\n```\n\n### Vcpkg\n\n```\nvcpkg install libassert\n```\n```cmake\nfind_package(libassert CONFIG REQUIRED)\ntarget_link_libraries(YOUR_TARGET PRIVATE libassert::assert)\n```\n\n# Platform Logistics\n\nWindows and macos require a little extra work to get everything in the right place\n\nCopying the library .dll on windows:\n\n```cmake\n# Copy the assert.dll on windows to the same directory as the executable for your_target.\n# Not required if static linking.\nif(WIN32)\n  add_custom_command(\n    TARGET your_target POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy_if_different\n    $\u003cTARGET_FILE:libassert::assert\u003e\n    $\u003cTARGET_FILE_DIR:your_target\u003e\n  )\nendif()\n```\n\nOn macOS it's recommended to generate a dSYM file containing debug information for your program:\n\nIn xcode cmake this can be done with\n\n```cmake\nset_target_properties(your_target PROPERTIES XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT \"dwarf-with-dsym\")\n```\n\nAnd outside xcode this can be done with `dsymutil yourbinary`:\n\n```cmake\n# Create a .dSYM file on macos. Currently required, but hopefully not for long\nif(APPLE)\n  add_custom_command(\n    TARGET your_target\n    POST_BUILD\n    COMMAND dsymutil $\u003cTARGET_FILE:your_target\u003e\n  )\nendif()\n```\n\n# Replacing \u0026lt;cassert\u0026gt;\n\nThis library is not a drop-in replacement for `\u003ccassert\u003e`. `-DLIBASSERT_LOWERCASE` can be used to create lowercase aliases\nfor the assertion macros but be aware that libassert's `ASSERT` is still checked in release. To replace `\u003ccassert\u003e` use\nwith libassert, replace `assert` with `DEBUG_ASSERT` or create an alias along the following lines:\n\n```cpp\n#define assert(...) DEBUG_ASSERT(__VA_ARGS__)\n```\n\nOne thing to be aware: Overriding cassert's `assert` is technically [not allowed][16.4.5.3.3] by the standard, but this\nshould not be an issue for any sane compiler.\n\n# FAQ\n\n## Does it have spell-check? \u003c!-- omit in toc --\u003e\n\nNo, not yet.\n\n# Cool projects using libassert\n\n- [Morwenn's `cpp-sort`](https://github.com/Morwenn/cpp-sort)\n\n# Comparison With Other Languages\n\nEven with constructs like `assert_eq`, assertion diagnostics are often lacking. For example, in rust the left and right\nvalues are displayed but not the expressions themselves:\n\n```rust\nfn main() {\n    let count = 4;\n    assert_eq!(count, 2);\n}\n```\n```\nthread 'main' panicked at 'assertion failed: `(left == right)`\n  left: `4`,\n right: `2`', /app/example.rs:3:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n```\n\nThis is not as helpful as it could be.\n\nFunctionality other languages / their standard libraries provide:\n\n|                                    | C/C++ | Rust |  C#  | Java | Python | JavaScript | Libassert |\n| :--------------------------------- | :---: | :--: | :--: | :--: | :----: | :--------: | :-------: |\n| Expression string                  | ✔️  |  ❌  |  ❌  |  ❌  |   ❌   |     ❌     |   ✔️    |\n| Location                           | ✔️  | ✔️ | ✔️ | ✔️ |  ✔️  |    ✔️    |   ✔️    |\n| Stack trace                        |  ❌   | ✔️ | ✔️ | ✔️ |  ✔️  |    ✔️    |   ✔️    |\n| Assertion message                  | ❌**  | ✔️ | ✔️ | ✔️ |  ✔️  |    ✔️    |   ✔️    |\n| Extra diagnostics                  |  ❌   | ❌*  | ❌*  |  ❌  |  ❌*   |    ❌*     |   ✔️    |\n| Binary specializations             |  ❌   | ✔️ |  ❌  |  ❌  |   ❌   |    ✔️    |   ✔️    |\n| Automatic expression decomposition |  ❌   |  ❌  |  ❌  |  ❌  |   ❌   |     ❌     |   ✔️    |\n| Sub-expression strings             |  ❌   |  ❌  |  ❌  |  ❌  |   ❌   |     ❌     |   ✔️    |\n\n`*`: Possible through string formatting but that is sub-ideal. \u003cbr/\u003e\n`**`: `assert(expression \u0026\u0026 \"message\")` is commonly used but this is sub-ideal and only allows string literal messages.\n\nExtras:\n\n|                                                                                         | C/C++ | Rust | C#  | Java | Python | JavaScript | Libassert |\n| :-------------------------------------------------------------------------------------- | :---: | :--: | :-: | :--: | :----: | :--------: | :-------: |\n| Syntax highlighting                                                                     |  ❌   |  ❌  | ❌  |  ❌  |  🟡   |     ❌     |   ✔️    |\n| Literal formatting consistency                                                          |  ❌   |  ❌  | ❌  |  ❌  |   ❌   |     ❌     |   ✔️    |\n| Expression strings and expression values everywhere                                     |  ❌   |  ❌  | ❌  |  ❌  |   ❌   |     ❌     |   ✔️    |\n| Return values from the assert to allow asserts to be integrated into expressions inline |  ❌   |  ❌  | ❌  |  ❌  |   ❌   |     ❌     |   ✔️    |\n\n[16.4.5.3.3]: https://eel.is/c++draft/reserved.names#macro.names-1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremy-rifkin%2Flibassert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeremy-rifkin%2Flibassert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremy-rifkin%2Flibassert/lists"}