{"id":24600788,"url":"https://github.com/renatoGarcia/icecream-cpp","last_synced_at":"2025-10-05T21:32:06.759Z","repository":{"id":37378535,"uuid":"168962039","full_name":"renatoGarcia/icecream-cpp","owner":"renatoGarcia","description":"🍦 Never use cout/printf to debug again","archived":false,"fork":false,"pushed_at":"2024-04-05T00:30:17.000Z","size":493,"stargazers_count":522,"open_issues_count":10,"forks_count":30,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-04-05T01:52:35.576Z","etag":null,"topics":["cpp","cpp11","cpp14","cpp17","debug","debugging","debugging-tool","header-only","print","single-header-lib"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/renatoGarcia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null}},"created_at":"2019-02-03T15:49:38.000Z","updated_at":"2024-04-15T05:17:04.579Z","dependencies_parsed_at":"2024-04-05T01:33:33.953Z","dependency_job_id":"a021b130-57b0-45da-84e1-989ab3d468b9","html_url":"https://github.com/renatoGarcia/icecream-cpp","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renatoGarcia%2Ficecream-cpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renatoGarcia%2Ficecream-cpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renatoGarcia%2Ficecream-cpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renatoGarcia%2Ficecream-cpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/renatoGarcia","download_url":"https://codeload.github.com/renatoGarcia/icecream-cpp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235448520,"owners_count":18991891,"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":["cpp","cpp11","cpp14","cpp17","debug","debugging","debugging-tool","header-only","print","single-header-lib"],"created_at":"2025-01-24T14:01:33.068Z","updated_at":"2025-10-05T21:32:06.746Z","avatar_url":"https://github.com/renatoGarcia.png","language":"C++","readme":"# Icecream-cpp\n\n[![CI.badge]][CI.link]\n[![LICENSE.badge]][LICENSE.link]\n\nIcecream-cpp is a little (single header) library to help with the print debugging in C++11\nand forward.\n\n[Try it at Compiler Explorer!](https://godbolt.org/z/WKbxejjaa)\n\n\n**Contents**\n* [Rationale](#rationale)\n* [Install](#install)\n  * [Conan](#conan)\n  * [Nix](#nix)\n* [Usage](#usage)\n  * [Direct printing](#direct-printing)\n  * [Range views pipeline](#range-views-pipeline)\n  * [Return value and Icecream apply macro](#return-value-and-icecream-apply-macro)\n  * [Output formatting](#output-formatting)\n  * [C strings](#c-strings)\n  * [Character Encoding](#character-encoding)\n  * [Macro disabling](#macro-disabling)\n  * [Configuration](#configuration)\n     * [enable/disable](#enabledisable)\n     * [output](#output)\n     * [prefix](#prefix)\n     * [show_c_string](#show_c_string)\n     * [decay_char_array](#decay_char_array)\n     * [force_range_strategy](#force_range_strategy)\n     * [force_tuple_strategy](#force_tuple_strategy)\n     * [force_variant_strategy](#force_variant_strategy)\n     * [wide_string_transcoder](#wide_string_transcoder)\n     * [unicode_transcoder](#unicode_transcoder)\n     * [output_transcoder](#output_transcoder)\n     * [line_wrap_width](#line_wrap_width)\n     * [include_context](#include_context)\n     * [context_delimiter](#context_delimiter)\n  * [Printing strategies](#printing-strategies)\n     * [IOStreams](#iostreams)\n     * [Formatting library](#formatting-library)\n     * [{fmt}](#fmt-1)\n     * [Characters](#characters)\n     * [Strings](#strings)\n     * [Pointer like types](#pointer-like-types)\n     * [Range types](#range-types)\n     * [Tuple like types](#tuple-like-types)\n     * [Optional types](#optional-types)\n     * [Variant types](#variant-types)\n     * [Exception types](#exception-types)\n     * [Clang dump struct](#clang-dump-struct)\n  * [Third-party libraries](#third-party-libraries)\n* [Pitfalls](#pitfalls)\n\nWith Icecream-cpp, an execution inspection:\n\n```c++\nauto my_function(int i, double d) -\u003e void\n{\n    std::cout \u003c\u003c \"1\" \u003c\u003c std::endl;\n    if (condition)\n        std::cout \u003c\u003c \"2\" \u003c\u003c std::endl;\n    else\n        std::cout \u003c\u003c \"3\" \u003c\u003c std::endl;\n}\n```\n\ncan be [coded instead](#direct-printing):\n\n```c++\nauto my_function(int i, double d) -\u003e void\n{\n    IC();\n    if (condition)\n        IC();\n    else\n        IC();\n}\n```\n\nand will print something like:\n\n    ic| test.cpp:34 in \"void my_function(int, double)\"\n    ic| test.cpp:36 in \"void my_function(int, double)\"\n\nAlso, any variable inspection like:\n\n```c++\nstd::cout \u003c\u003c \"a: \" \u003c\u003c a\n          \u003c\u003c \", b: \" \u003c\u003c b\n          \u003c\u003c \", sum(a, b): \" \u003c\u003c sum(a, b)\n          \u003c\u003c std::endl;\n```\n\ncan be [simplified to](#direct-printing):\n\n```c++\nIC(a, b, sum(a, b));\n```\n\nand will print:\n\n    ic| a: 7, b: 2, sum(a, b): 9\n\nWe also can inspect the data flowing through a range views pipeline (both [STL\nranges](https://en.cppreference.com/w/cpp/header/ranges) and\n[Range-v3](https://ericniebler.github.io/range-v3/)), by inserting a\n[`IC_V()`](#range-views-pipeline) function at the point of interest:\n\n```c++\nauto rv = std::vector\u003cint\u003e{1, 0, 2, 3, 0, 4, 5}\n    | vws::split(0)\n    | IC_V()\n    | vws::enumerate;\n```\n\nSo that when we iterate on `rv`, we will see the printing:\n\n    ic| range_view_63:16[0]: [1]\n    ic| range_view_63:16[1]: [2, 3]\n    ic| range_view_63:16[2]: [4, 5]\n\n\nAnd as a last example, we can easily [format the printing output](#output-formatting):\n\n```c++\nauto i = int{42};\nIC_F(\"#x\", i);\nauto v = std::vector\u003cchar8_t\u003e{'a', 'b', 'c', 'd', 'e', 'f'};\nIC_F(\"[1:-2]\", v);\n```\n\nwill print:\n\n```\nic| i: 0x2a\nic| v: [1:-2]-\u003e['b', 'c', 'd']\n```\n\nThis library was inspired by the original [Python\nIceCream](https://github.com/gruns/icecream) library.\n\n## Rationale\n\nThe Icecream-cpp is a throw off library. It is designed to be used when writing code and\ndebugging it, it was not designed to go along with the released product.\n\nBecause of these design decisions, the Icecream-cpp library perhaps is one of the few\nlibraries that you should spend more time writing code with it than reading code with it.\nWith that in mind, we have as design goals: expressivity and conciseness. It should be\npossible to print the value of any variable, formatted as desired, and writing the code to\nachieve that should be as easy and short as possible.\n\n## Install\n\nThe Icecream-cpp is a one file, header only library, having the STL as its only\ndependency. The most immediate way to use it is just copy the `icecream.hpp` header into\nyour project.\n\nTo properly install it system wide, together with the CMake project files, run these\ncommands at Icecream-cpp project root directory:\n\n```Shell\nmkdir build\ncd build\ncmake ..\ncmake --install .\n```\n\n### Conan\n\nThe released versions are available on [Conan](https://conan.io/center/recipes/icecream-cpp) too:\n\n[Here](example_project/conanfile.txt) we also have an example of a `conanfile.txt` which\nrequires Icecream-cpp.\n\n### Nix\n\nIf using [Nix](https://nixos.org), Icecream-cpp can be included as a flakes input as\n```Nix\ninputs.icecream-cpp.url = \"github:renatoGarcia/icecream-cpp\";\n```\n\nThe Icecream-cpp flake defines an overlay, so that it can be used when importing `nixpkgs`:\n```Nix\nimport nixpkgs {\n  system = \"x86_64-linux\";\n  overlays = [\n    icecream-cpp.overlays.default\n  ];\n}\n```\nDoing this, an `icecream-cpp` derivation will be added to the `nixpkgs` attribute set.\n\nA working example of how to use Icecream-cpp in a flake project is available\n[here](example_project/flake.nix).\n\n\n## Usage\n\nIf using CMake:\n```CMake\nfind_package(icecream-cpp)\ntarget_link_libraries(\u003ctarget\u003e PRIVATE icecream-cpp::icecream-cpp)\n```\nWhere `\u003ctarget\u003e` is the executable or library being compiled.\n\nAfter including the `icecream.hpp` header in a source file:\n\n```C++\n#include \u003cicecream.hpp\u003e\n```\n\nall the functionalities of Icecream-cpp library will be available by the functions\n[`IC`](#direct-printing), [`IC_A`](#return-value-and-icecream-apply-macro), and\n[`IC_V`](#range-views-pipeline); together with its respective counterparts `IC_F`,\n`IC_FA`, and `IC_FV`; that behave the same but accept an [output formatting\nstring](#output-formatting) as its first argument.\n\n\n### Direct printing\n\nThe `IC` is the simplest of the Icecream-cpp functions. If called with no arguments it\nwill print the [prefix](#prefix), the source file name, the current line number, and the\ncurrent function signature. The code:\n\n```C++\nauto my_function(int foo, double bar) -\u003e void\n{\n    // ...\n    IC();\n    // ...\n}\n```\n\nwill print:\n\n    ic| test.cpp:34 in \"void my_function(int, double)\"\n\nIf called with arguments it will print the [prefix](#prefix), those arguments names, and\nits values.  The code:\n\n```C++\nauto v0 = std::vector\u003cint\u003e{1, 2, 3};\nauto s0 = std::string{\"bla\"};\nIC(v0, s0, 3.14);\n```\n\nwill print:\n\n    ic| v0: [1, 2, 3], s0: \"bla\", 3.14: 3.14\n\nThe variant `IC_F` behaves the same as the `IC` function, but accepts an [output\nformatting string](#output-formatting) as its first argument.\n\n\n### Range views pipeline\n\nTo print the data flowing through a range views pipeline (both with [STL\nranges](https://en.cppreference.com/w/cpp/header/ranges) and\n[Range-v3](https://ericniebler.github.io/range-v3/)), we can use either the `IC_V` or\n`IC_FV` functions, which will lazily print any input coming from the previous view. The\n`IC_VF` function behaves the same as `IC_V`, but accepts the same [format\nstring](#output-formatting) as the [*range format string*](#range-format-string) as its\nfirst argument. Since these functions will be placed within a range views pipeline, the\nprinting will be done lazily, while each element is generated. For instance:\n\n```C++\nnamespace vws = std::views;\nauto v0 = vws::iota('a') | vws::enumerate | IC_V() | vws::take(3);\nfor (auto e : v0)\n{\n    //...\n}\n```\n\nIn this code nothing will be immediately printed when `v0` is created, just when iterating\nover it. At each `for` loop iteration one line will be printed, until we have the output:\n\n    ic| range_view_61:53[0]: (0, 'a')\n    ic| range_view_61:53[1]: (1, 'b')\n    ic| range_view_61:53[2]: (2, 'c')\n\n\u003e [!NOTE]\n\u003e The Icecream-cpp will enable its support to Range-v3 types either if the \"icecream.hpp\"\n\u003e header is included some lines after any Range-v3 header, or if the `ICECREAM_RANGE_V3`\n\u003e macro was declared before the \"icecream.hpp\" header inclusion. This is discussed in\n\u003e details at the [*third-party libraries*](#third-party-libraries) section.\n\nThe `IC_V` function has the signature `IC_V(name, projection)`, and the `IC_FV` function\n`IC_FV(fmt, name, projection)`. In both them, the `name` and `projection` parameters as\noptional.\n\n#### fmt\n\nThe same syntax as described at [*range types format string*](#range-format-string).\n\n#### name\n\nThe variable name used by the view when printing. The printing layout is: `\u003cname\u003e[\u003cidx\u003e]:\n\u003cvalue\u003e`. If the name parameter is not used, the default value to `\u003cname\u003e` is\n`range_view_\u003csource_location\u003e`.\n\nThe code:\n\n```C++\nvws::iota('a') | vws::enumerate | IC_V(\"foo\") | vws::take(2);\n```\n\nwhen iterated over will print:\n\n    ic| foo[0]: (0, 'a')\n    ic| foo[1]: (1, 'b')\n\n\n#### projection\n\nA [callable](https://en.cppreference.com/w/cpp/named_req/Callable) that will receive as\ninput a *const reference* to the current element at the *range views pipeline*, and must\nreturn the actual value to be printed.\n\nThe code:\n\n```C++\nvws::iota('a') | vws::enumerate | IC_V([](auto e){return std::get\u003c1\u003e(e);}) | vws::take(2);\n```\n\nwhen iterated over will print:\n\n    ic| range_view_61:53[0]: 'a'\n    ic| range_view_61:53[1]: 'b'\n\n\u003e [!NOTE]\n\u003e The `IC_V` function will forward to the next view the exact same input element, just as\n\u003e it was received from the previous view. No action done by the `projection` function will\n\u003e have any effect on that.\n\n\n### Return value and Icecream apply macro\n\nThe [`IC`](#direct-printing) function (and [`IC_F`](#output-formatting)) won't return any\nvalue in its general case. Exception to that if called with exactly one argument, when\nthen it will return the argument itself.\n\nThis is done this way so we can use `IC` to inspect a function argument at calling point,\nwithout further code change. In the code:\n\n```C++\nmy_function(IC(MyClass{}));\n```\nthe `MyClass` object will be forwarded to `my_function` as if the `IC` function wasn't\nthere. The `my_function` will continue receiving a rvalue reference to a `MyClass` object.\n\nThis approach however is not so practical when the function has multiple arguments. In the code:\n```C++\nanother_function(IC(a), IC(b), IC(c), IC(d));\n```\nbesides writing four times the `IC` function, the printed output will be split in four\nlines. Something like:\n\n    ic| a: 1\n    ic| b: 2\n    ic| c: 3\n    ic| d: 4\n\nTo work around that, there is the `IC_A` function. `IC_A` behaves exactly like the `IC`\nfunction, but receives a [callable](https://en.cppreference.com/w/cpp/named_req/Callable)\nas its first argument, and will call it using the remaining arguments, printing all of\nthem before that. That previous example code could be rewritten as:\n\n```C++\nIC_A(another_function, a, b, c, d);\n```\n\nand this time it will print:\n\n    ic| a: 1, b: 2, c: 3, d: 4\n\nThe `IC_A` function will return the same value returned by the callable. The code:\n\n```C++\nauto mc = std::make_unique\u003cMyClass\u003e();\nauto r = IC_A(mc-\u003emy_function, a, b);\n```\n\nbehaves exactly the same as:\n\n```C++\nauto mc = std::make_unique\u003cMyClass\u003e();\nauto r = mc-\u003emy_function(a, b);\n```\n\nbut will print the values of `a` and `b`.\n\nThe `IC_FA` variant does the same as the `IC_A` function, but accepts an [output\nformatting string](#output-formatting) as its first argument, just before the callable\nargument.\n\n\n### Output formatting\n\nIt is possible to configure how a value must be formatted while printing. The following\ncode:\n\n```C++\nauto a = int{42};\nauto b = int{20};\nIC_F(\"#X\", a, b);\n```\n\nwill print:\n\n    ic| a: 0X2A, b: 0X14\n\nif using the `IC_F` variant instead of the plain [`IC`](#direct-printing) function. A\nsimilar result would be achieved if using `IC_FA` and `IC_FV` in place of\n[`IC_A`](#return-value-and-icecream-apply-macro) and [`IC_V`](#range-views-pipeline)\nrespectively.\n\nWhen using the formatting function variants (`IC_F` and `IC_FA`), the same formatting\nstring will be applied by default to all of its arguments. That could be a problem if we\nneed to have distinct formatting to each argument, or if the arguments have multiple types\nwith non mutually valid syntaxes. Therefore, to set a distinct formatting string to a\nspecific argument we can wrap it with the `IC_` function. The code:\n\n```C++\nauto a = int{42};\nauto b = int{20};\nIC_F(\"#X\", a, IC_(\"d\", b));\n```\n\nwill print:\n\n    ic| a: 0X2A, b: 20\n\nThe `IC_` function can also be used within the plain `IC` (or `IC_A`) function:\n\n```C++\nauto a = int{42};\nauto b = int{20};\nIC(IC_(\"#x\", a), b);\n```\n\nwill print:\n\n    ic| a: 0x2a, b: 20\n\nThe last argument in an `IC_` function call is the one that will be printed, all other\narguments coming before the last will be converted to a string using the\n[`to_string`](https://en.cppreference.com/w/cpp/string/basic_string/to_string) function\nand concatenated to the resulting formatting string.\n\n```C++\nauto a = float{1.234};\nauto width = int{7};\nIC(IC_(\"*\u003c\",width,\".3\", a));\n```\n\nWill have as result a formatting string `\"*\u003c7.3\"`, and will print:\n\n    ic| a: 1.23***\n\nJust for completeness in the examples, an usage of `IC_FA` and `IC_FV` would be:\n\n```C++\nIC_FA(\"#x\", my_function, 10, 20);\nauto rv0 = vws::iota(0) | IC_FV(\"[::2]:#x\", \"bar\") | vws::take(5);\n```\n\nThis will at first print:\n\n    ic| 10: 0xa, 20: 0x14\n\nand when iterating on `rv0`:\n\n    ic| bar[0]: 0\n    ic| bar[2]: 0x2\n    ic| bar[4]: 0x4\n\nTo `IC_F` and `IC_FA`, the syntax specification of the formatting strings depends both on\nthe type `T` being printed, and in that type's [printing strategy](#printing-strategies)\nused by Icecream-cpp.\n\nTo `IC_FV`, the formatting syntax if the same as the [Range format\nstring](#range-format-string).\n\n\n### C strings\n\nC strings are ambiguous. Should a `char* foo` variable be interpreted as a pointer to a\nsingle `char` or as a null-terminated string? Likewise, is the `char bar[]` variable an\narray of single characters or a null-terminated string? Is `char baz[3]` an array with\nthree single characters or is it a string of size two plus a `'\\0'`?\n\nEach one of these interpretations of `foo`, `bar`, and `baz` would be printed in a\ndistinct way. To the code:\n\n```C++\nchar flavor[] = \"pistachio\";\nIC(flavor);\n```\n\nall three outputs below are correct, each one having a distinct interpretation of what\nshould be the `flavor` variable.\n\n```\nic| flavor: 0x55587b6f5410\nic| flavor: ['p', 'i', 's', 't', 'a', 'c', 'h', 'i', 'o', '\\u{0}']\nic| flavor: \"pistachio\"\n```\n\nA bounded `char` array (i.e.: array with a known size) will either be interpreted as an\narray of single characters or let decay to a `char*`, subject to the\n[`decay_char_array`](#decay_char_array) option.\n\n\nAn unbounded `char[]` arrays (i.e.: array with an unknown size) will decay to `char*`\npointers, and a character pointer will be printed either as a string or as a pointer as\nconfigured by the [`show_c_string`](#show_c_string) option.\n\nThe exact same logic as above applies to C strings of all character types, namely `char`,\n`wchar_t`, `char8_t`, `char16_t`, and `char32_t`.\n\n\n### Character Encoding\n\nCharacter encoding in C++ is messy.\n\nThe `char8_t`, `char16_t`, and `char32_t` strings are well defined. They are capable, and\ndo hold Unicode code units of 8, 16, and 32 bits respectively, and they are encoded in\nUTF-8, UTF-16, and UTF-32 also respectively.\n\nThe `char` strings have a well defined code unit bit size (given by\n[`CHAR_BIT`](https://en.cppreference.com/w/cpp/types/climits), usually 8 bits), but there\nare no requirements about its encoding.\n\nThe `wchar_t` strings have [neither a well defined code unit\nsize](https://en.cppreference.com/w/cpp/language/types#Character_types), nor any\nrequirements about its encoding.\n\nIn a code like this:\n\n```C++\nauto const str = std::string{\"foo\"};\nstd::cout \u003c\u003c str;\n```\n\nWe will have three character encoding points of interest. In the first one, before\ncompiling, that code will be written in a source file in an unspecified *source encoding*.\nIn the second interest point, the compiled binary will have the \"foo\" string stored in an\nalso unspecified *execution encoding*. Finally on the third point of interest, the \"foo\"\nbyte stream sent to `std::cout` will be ultimately forwarded to the system, that expects\nthat stream being encoded in an also unspecified *output encoding*.\n\nFrom these three *interest points of character encoding*, both *execution encoding* and\n*output encoding* have impact in the inner working of Icecream-cpp, and there is no way to\nknow for sure what is the used encoding in both of them. In face of this uncertainty, the\nadopted strategy is to offer a reasonable default transcoding function, that will try\nconvert the data to the right encoding, and allow the user to use its own implementation\nwhen needed.\n\nExcept for wide and Unicode string types (discussed below), when printing any other types\nwe will have its serialized textual data in *execution encoding*. That *execution\nencoding* may or may not be the same as the *output encoding*, this one being the encoding\nexpected by the configured [output](#output). Because of that, before we send that data to\nthe output, we must transcode it to make sure that we have it in *output encoding*. To\nthat end, before delivering the text data to the [output](#output), we send it to the\nconfigured [`output_transcoder`](#output_transcoder) function, that must ensure it is\nencoded in the correct *output encoding*.\n\nWhen printing the wide and Unicode string types, we need to have one more transcoding\nlevel, because it is possible that the text data is in a distinct character encoding from\nthe expected *execution encoding*. Because of that, additional logic is applied to make\nsure that the strings are in *execution encoding* before we send them to output. That is\ndone by applying the [`wide_string_transcoder`](#wide_string_transcoder) and\n[`unicode_transcoder`](#unicode_transcoder) functions respectively.\n\n### Macro disabling\n\nAll the Icecream-cpp printing will be disabled in a translation unit if the\n`ICECREAM_DISABLE` macro is defined before the `icecream.hpp` header inclusion.\n\nThis can be done either by defining it in the source code:\n\n```C++\n#define ICECREAM_DISABLE\n#include \u003cicecream.hpp\u003e\n```\n\nor as an argument to the compiler. A `-DICECREAM_DISABLE` with\n[GCC](https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html#index-D-1) and\n[Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-D-macro),\nand a `/DICECREAM_DISABLE` in\n[MSVC](https://learn.microsoft.com/en-us/cpp/build/reference/d-preprocessor-definitions)\nfor example.\n\nThis will disable only the printing output, all other functionalities will still work. In\nparticular, all the changes to the global [`IC_CONFIG`](#configuration) will be effective.\n\n### Configuration\n\nThe Icecream-cpp configuration system works \"layered by scope\". At the basis level we have\nthe global `IC_CONFIG` object. That global instance is shared by the whole running\nprogram, as would be expected of a global variable. It is created with all config options\nat its default values, and any change is readily seen by the whole program.\n\nAt any point in the code we can create a new config layer in the current scope by\ninstantiating a new `IC_CONFIG` variable, calling the `IC_CONFIG_SCOPE()` macro function.\nAll the config options of this new instance will be at an \"unset\" state by default, and\nany request to retrieve an option value not yet set will be delegated to its parent. That\nrequest will go up on the parent chain until the first one having that option set answers.\n\nAll config options are set by using accessor methods of the `IC_CONFIG` object, and they\ncan be chained:\n\n```C++\nIC_CONFIG\n    .prefix(\"ic: \")\n    .show_c_string(false)\n    .line_wrap_width(70);\n```\n\n`IC_CONFIG` is just a regular variable with a funny name to make a collision extremely\nunlikely. When calling any `IC*(...)` macro, it will pick the `IC_CONFIG` instance at\ncurrent scope by doing an [unqualified name\nlookup](https://en.cppreference.com/w/cpp/language/unqualified_lookup), using the same\nrules applied to any other regular variable.\n\nTo summarize all the above, in the code:\n\n```C++\nauto my_function() -\u003e void\n{\n    IC_CONFIG.line_wrap_width(20);\n\n    IC_CONFIG_SCOPE();\n    IC_CONFIG.context_delimiter(\"|\");\n    IC_CONFIG.show_c_string(true);\n\n    {\n        IC_CONFIG_SCOPE();\n        IC_CONFIG.show_c_string(false);\n        // A\n    }\n    // B\n}\n```\n\nAt line `A`, the value of `IC_CONFIG`'s `line_wrap_width`, `context_delimiter`, and\n`show_c_string` will be respectively: `20`, `\"|\"`, and `false`.\n\nAfter the closing of the innermost scope block, at line `B`, the value of `IC_CONFIG`'s\n`line_wrap_width`, `context_delimiter`, and `show_c_string` will be respectively: `20`,\n`\"|\"`, and `true`.\n\nThe reading and writing operations on `IC_CONFIG` objects are thread safe.\n\n\u003e [!NOTE]\n\u003e Any modification in an `IC_CONFIG`, other than to the global instance, will be seen only\n\u003e within the current scope. As consequence, those modifications won't propagate to the\n\u003e scope of any called function.\n\n\n#### enable/disable\n\nEnable or disable the output of `IC(...)` macro. The default value is *enabled*.\n\n- get:\n    ```C++\n    auto is_enabled() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto enable() -\u003e Config\u0026;\n    auto disable() -\u003e Config\u0026;\n    ```\n\nThe code:\n\n```C++\nIC(1);\nIC_CONFIG.disable();\nIC(2);\nIC_CONFIG.enable();\nIC(3);\n```\n\nwill print:\n\n```\nic| 1: 1\nic| 3: 3\n```\n\n#### output\n\nSets where the serialized textual data will be printed. By default that data will be\nprinted in the *standard error output*, the same as `std::cerr`.\n\n- set:\n    ```C++\n    template \u003ctypename T\u003e\n    auto output(T\u0026\u0026 t) -\u003e Config\u0026;\n    ```\n\nWhere the type `T` must be one of:\n- A class inheriting from `std::ostream`,\n- A class having a method `push_back(char)`,\n- An output iterator that accepts the operation `*it = 'c'`.\n\nFor instance, the code:\n```C++\nauto str = std::string{};\nIC_CONFIG.output(str);\nIC(1, 2);\n```\nWill print the output `\"ic| 1: 1, 2: 2\\n\"` in the `str` string.\n\n\u003e [!WARNING]\n\u003e Icecream-cpp won't take ownership of the `t` argument, so care must be taken by the user\n\u003e to ensure that it stay alive.\n\n#### prefix\n\nA function that generates the text that will be printed before each output.\n\n- set:\n    ```C++\n    template \u003ctypename... Ts\u003e\n    auto prefix(Ts\u0026\u0026 ...values) -\u003e Config\u0026;\n    ```\n\nWhere each one of the types `Ts` must be one of:\n- A string,\n- A callable `T() -\u003e U`, where `U` has an overload of `operator\u003c\u003c(ostream\u0026, U)`.\n\nThe printed prefix will be a concatenation of all those elements.\n\nThe code:\n```C++\nIC_CONFIG.prefix(\"icecream| \");\nIC(1);\nIC_CONFIG.prefix([]{return 42;}, \"- \");\nIC(2);\nIC_CONFIG.prefix(\"thread \", std::this_thread::get_id, \" | \");\nIC(3);\n```\n\nwill print:\n\n```\nicecream| 1: 1\n42- 2: 2\nthread 1 | 3: 3\n```\n\n#### show_c_string\n\nControls if a character pointer variable (`char*` `wchar_t*`, `char8_t*`, `char16_t*`, or\n`char32_t*`) should be interpreted as a null-terminated C string (`true`) or a pointer to\na `char` (`false`). The default value is `true`.\n\n- get:\n    ```C++\n    auto show_c_string() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto show_c_string(bool value) -\u003e Config\u0026;\n    ```\n\nThe code:\n\n```C++\nchar const* flavor = \"mango\";\n\nIC_CONFIG.show_c_string(true);\nIC(flavor);\n\nIC_CONFIG.show_c_string(false);\nIC(flavor);\n```\n\nwill print:\n\n```\nic| flavor: \"mango\";\nic| flavor: 0x55587b6f5410\n```\n\n#### decay_char_array\n\nControls if a character array variable (`char[N]` `wchar_t[N]`, `char8_t[N]`,\n`char16_t[N]`, or `char32_t[N]`) should decay to a character pointer (when `true`) to be\nprinted by the [*strings strategy*](#strings) (subject to the\n[`show_c_string`](#show_c_string) configuration), or remain as an array (when `false`) to\nbe printed by the [*range types*](#range-types) strategy.\n\nThe default value is `false`.\n\n- get:\n    ```C++\n    auto decay_char_array() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto decay_char_array(bool value) -\u003e Config\u0026;\n    ```\n\nThe code:\n\n```C++\nchar flavor[] = \"caju\";\n\nIC_CONFIG.decay_char_array(true);\nIC(flavor);\n\nIC_CONFIG.decay_char_array(false);\nIC(flavor);\n```\n\nwill print:\n\n```\nic| flavor: \"caju\";\nic| flavor: ['c', 'a', 'j', 'u', '\\u{0}']\n```\n\n#### force_range_strategy\n\nControls if a range type `T` will be printed using the [range type](#range-types) strategy\neven when the [Formatting](https://en.cppreference.com/w/cpp/utility/format) or\n[{fmt}](https://fmt.dev) libraries would be able to print it. We force the use of *range\ntype* strategy here because it supports more useful formatting options that would be\navailable otherwise if using some *baseline strategy*.\n\nThis option has a default value of `true`.\n\n- get:\n    ```C++\n    auto force_range_strategy() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto force_range_strategy(bool value) -\u003e Config\u0026;\n    ```\n\n#### force_tuple_strategy\n\nControls if a tuple like type `T` will be printed using the [tuple like\ntypes](#tuple-like-types) strategy even when the\n[Formatting](https://en.cppreference.com/w/cpp/utility/format) or [{fmt}](https://fmt.dev)\nlibraries would be able to print it. We force the use of *tuple like types* strategy here\nbecause it supports more useful formatting options that would be available otherwise if\nusing some *baseline strategy*.\n\nThis option has a default value of `true`.\n\n- get:\n    ```C++\n    auto force_tuple_strategy() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto force_tuple_strategy(bool value) -\u003e Config\u0026;\n    ```\n\n#### force_variant_strategy\n\nControls if a variant type `T`\n([`std::variant`](https://en.cppreference.com/w/cpp/utility/variant) or\n[`boost::variant2::variant`](https://www.boost.org/doc/libs/release/libs/variant2/doc/html/variant2.html))\nwill be printed using the [variant types](#variant-types) strategy even when some of the\n*baseline strategies* would be able to print it. We force the use of *variant types*\nstrategy here because it supports more useful formatting options that would be available\notherwise if using some *baseline strategy*.\n\nThis option has a default value of `true`.\n\n- get:\n    ```C++\n    auto force_variant_strategy() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto force_variant_strategy(bool value) -\u003e Config\u0026;\n    ```\n\n#### wide_string_transcoder\n\nFunction that transcodes a `wchar_t` string, from a system defined encoding to a `char`\nstring in the system *execution encoding*.\n\n- set:\n    ```C++\n    auto wide_string_transcoder(std::function\u003cstd::string(wchar_t const*, std::size_t)\u003e transcoder) -\u003e Config\u0026;\n    auto wide_string_transcoder(std::function\u003cstd::string(std::wstring_view)\u003e transcoder) -\u003e Config\u0026;\n    ```\n\nThere is no guarantee that the input string will end in a null terminator (this is the\nactual semantic of a\n[`string_view`](https://en.cppreference.com/w/cpp/string/basic_string_view)), so the user\nmust observe the input string size value.\n\nThe default implementation will check if the C locale is set to other value than \"C\" or\n\"POSIX\". If yes, it will forward the input to the\n[std::wcrtomb](https://en.cppreference.com/w/cpp/string/multibyte/wcrtomb) function.\nOtherwise, it will assume that the input is Unicode encoded (UTF-16 or UTF-32, accordingly\nto the byte size of `wchar_t`), and transcoded it to UTF-8.\n\n#### unicode_transcoder\n\nFunction that transcodes a `char32_t` string, from a UTF-32 encoding to a `char` string in\nthe system *execution encoding*.\n\n- set:\n    ```C++\n    auto unicode_transcoder(std::function\u003cstd::string(char32_t const*, std::size_t)\u003e transcoder) -\u003e Config\u0026;\n    auto unicode_transcoder(std::function\u003cstd::string(std::u32string_view)\u003e transcoder) -\u003e Config\u0026;\n    ```\n\nThere is no guarantee that the input string will end on a null terminator (this is the\nactual semantic of a\n[`string_view`](https://en.cppreference.com/w/cpp/string/basic_string_view)), so the user\nmust observe the input string size value.\n\nThe default implementation will check the C locale is set to other value than \"C\" or\n\"POSIX\". If yes, it will forward the input to the\n[std::c32rtomb](https://en.cppreference.com/w/cpp/string/multibyte/c32rtomb) function.\nOtherwise, it will just transcoded it to UTF-8.\n\nThis function will be used to transcode all the `char8_t`, `char16_t`, and `char32_t`\nstrings.  When transcoding `char8_t` and `char16_t` strings, they will be first converted\nto a `char32_t` string, before being sent as input to this function.\n\n#### output_transcoder\n\nFunction that transcodes a `char` string, from the system *execution encoding* to a `char`\nstring in the system *output encoding*, as expected by the configured [`output`](#output).\n\n- set:\n    ```C++\n    auto output_transcoder(std::function\u003cstd::string(char const*, std::size_t)\u003e transcoder) -\u003e Config\u0026;\n    auto output_transcoder(std::function\u003cstd::string(std::string_view)\u003e transcoder) -\u003e Config\u0026;\n    ```\n\nThere is no guarantee that the input string will end on a null terminator (this is the\nactual semantic of a\n[`string_view`](https://en.cppreference.com/w/cpp/string/basic_string_view)), so the user\nmust observe the input string size value.\n\nThe default implementation assumes that the *execution encoding* is the same as the\n*output encoding*, and will return an unchanged input.\n\n#### line_wrap_width\n\nThe maximum number of characters before the output be broken in multiple lines. Default\nvalue of `70`.\n\n- get:\n    ```C++\n    auto line_wrap_width() const -\u003e std::size_t;\n    ```\n- set:\n    ```C++\n    auto line_wrap_width(std::size_t value) -\u003e Config\u0026;\n    ```\n\n#### include_context\n\nIf the context (source name, line number, and function name) should be printed even when\nprinting variables. Default value is `false`.\n\n- get:\n    ```C++\n    auto include_context() const -\u003e bool;\n    ```\n- set:\n    ```C++\n    auto include_context(bool value) -\u003e Config\u0026;\n    ```\n\n#### context_delimiter\n\nThe string separating the context text from the variables values. Default value is `\"- \"`.\n\n- get:\n    ```C++\n    auto context_delimiter() const -\u003e std::string;\n    ```\n- set:\n    ```C++\n    auto context_delimiter(std::string const\u0026 value) -\u003e Config\u0026;\n    ```\n\n\n### Printing strategies\n\nIn order to be printable, a type `T` must satisfy at least one of the strategies described\nin the next sections. If a type `T` is not printable, it can be mended by adding support\nto one of the *baseline strategies*: [IOStreams](#iostreams), [Formatting\nlibrary](#formatting-library), and [{fmt}](#fmt-1).\n\nIf it happens that multiple strategies are simultaneously satisfied, the one with the\nhigher precedence will be chosen. Within the *baseline strategies*, the precedence order\nis: *{fmt}*, *Formatting library*, and *IOStreams*. The precedence criteria other than\nthese are discussed in each strategy section.\n\n\n#### IOStreams\n\nUses the [STL IOStream](https://en.cppreference.com/w/cpp/io) library to print values. A\ntype `T` is eligible to this strategy if there exist a function overload\n[`operator\u003c\u003c(ostream\u0026,\n\u003cSOME_TYPE\u003e)`](https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt), where a\nvalue of type `T` is accepted as `\u003cSOME_TYPE\u003e`.\n\nWithin the *baseline strategies* the IOStreams has the lowest precedence. So if a type is\nsupported either by [{fmt}](#fmt-1) or [Formatting library](#formatting-library) they will\nbe used instead.\n\n##### IOStreams format string\n\nThe format string of IOStreams strategy is strongly based on the ones of [STL\nFormatting](https://en.cppreference.com/w/cpp/utility/format/spec) and\n[{fmt}](https://fmt.dev/11.0/syntax/#format-specification-mini-language).\n\nIt has the following specification:\n\n```\nformat_spec ::=  [[fill]align][sign][\"#\"][width][\".\" precision][type]\nfill        ::=  \u003ca character\u003e\nalign       ::=  \"\u003c\" | \"\u003e\" | \"^\"\nsign        ::=  \"+\" | \"-\"\nwidth       ::=  integer\nprecision   ::=  integer\ntype        ::=  \"a\" | \"A\" | \"b\" | \"B\" | \"c\" | \"d\" | \"e\" | \"E\" | \"f\" | \"F\" | \"g\" | \"G\" | \"o\" | \"s\" | \"x\" | \"X\" | \"?\"\ninteger     ::=  digit+\ndigit       ::=  \"0\"...\"9\"\n```\n\n###### [[fill]align]\n\nThe fill character can be any char. The presence of a fill character is signaled by the\ncharacter following it, which must be one of the alignment options. The meaning of the\nalignment options is as follows:\n\n| Symbol | Meaning                                                                                                                                                |\n|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `'\u003c'`  | Left align within the available space.                                                                                                                 |\n| `'\u003e'`  | Right align within the available space. This is the default.                                                                                           |\n| `'^'`  | Internally align the data, with the fill character being placed between the digits and either the base or sign. Applies to integer and floating-point. |\n\nNote that unless a minimum field width is defined, the field width will always be the same\nsize as the data to fill it, so that the alignment option has no meaning in this case.\n\n###### [sign]\n\nThe sign option is only valid for number types, and can be one of the following:\n\n| Symbol | Meaning                                                                 |\n|--------|-------------------------------------------------------------------------|\n| `'+'`  | A sign will be used for both nonnegative as well as negative numbers. |\n| `'-'`  | A sign will be used only for negative numbers. This is the default.   |\n\n###### [\"#\"]\n\nCauses the “alternate form” to be used for the conversion. The alternate form is defined\ndifferently for different types. This option is only valid for integer and floating-point\ntypes. For integers, when binary, octal, or hexadecimal output is used, this option adds\nthe prefix respective \"0b\" (\"0B\"), \"0\", or \"0x\" (\"0X\") to the output value. Whether the\nprefix is lower-case or upper-case is determined by the case of the type specifier, for\nexample, the prefix \"0x\" is used for the type 'x' and \"0X\" is used for 'X'. For\nfloating-point numbers the alternate form causes the result of the conversion to always\ncontain a decimal-point character, even if no digits follow it. Normally, a decimal-point\ncharacter appears in the result of these conversions only if a digit follows it. In\naddition, for 'g' and 'G' conversions, trailing zeros are not removed from the result.\n\n###### [width]\n\nA decimal integer defining the minimum field width. If not specified, then the field width\nwill be determined by the content.\n\n###### [\".\" precision]\n\nThe precision is a decimal number indicating how many digits should be displayed after the\ndecimal point for a floating-point value formatted with 'f' and 'F', or before and after\nthe decimal point for a floating-point value formatted with 'g' or 'G'. For non-number\ntypes the field indicates the maximum field size - in other words, how many characters\nwill be used from the field content. The precision is not allowed for integer, character,\nBoolean, and pointer values. Note that a C string must be null-terminated even if\nprecision is specified.\n\n###### [type]\n\nDetermines how the data should be presented.\n\nThe available string presentation types are:\n\n| Symbol | Meaning                                                            |\n|--------|--------------------------------------------------------------------|\n| `'s'`  | String format.                                                     |\n| `'?'`  | Debug format. The string is quoted and special characters escaped. |\n| none   | The same as `'?'`.                                                 |\n\nThe available character presentation types are:\n\n| Symbol | Meaning                                                               |\n|--------|-----------------------------------------------------------------------|\n| `'c'`  | Character format.                                                     |\n| `'?'`  | Debug format. The character is quoted and special characters escaped. |\n| none   | The same as `'?'`.                                                    |\n\nThe available integer presentation types are:\n\n| Symbol | Meaning                                                                                                                                                                   |\n|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `'b'`  | Binary format. Outputs the number in base 2. Using the '#' option with this type adds the prefix \"0b\" to the output value.                                                |\n| `'B'`  | Binary format. Outputs the number in base 2. Using the '#' option with this type adds the prefix \"0B\" to the output value.                                                |\n| `'c'`  | Character format. Outputs the number as a character.                                                                                                                      |\n| `'d'`  | Decimal integer. Outputs the number in base 10.                                                                                                                           |\n| `'o'`  | Octal format. Outputs the number in base 8.                                                                                                                               |\n| `'x'`  | Hex format. Outputs the number in base 16, using lower-case letters for the digits above 9. Using the '#' option with this type adds the prefix \"0x\" to the output value. |\n| `'X'`  | Hex format. Outputs the number in base 16, using upper-case letters for the digits above 9. Using the '#' option with this type adds the prefix \"0X\" to the output value. |\n\nInteger presentation types can also be used with character and boolean values with the\nonly exception that 'c' cannot be used with bool. Boolean values are formatted using\ntextual representation, either true or false, if the presentation type is not specified.\n\nThe available presentation types for floating-point values are:\n\n| Symbol | Meaning                                                                                                                                                                                                                                                                     |\n|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `'a'`  | Hexadecimal floating point format. Prints the number in base 16 with prefix \"0x\" and lower-case letters for digits above 9. Uses 'p' to indicate the exponent.                                                                                                              |\n| `'A'`  | Same as 'a' except it uses upper-case letters for the prefix, digits above 9 and to indicate the exponent.                                                                                                                                                                  |\n| `'e'`  | Exponent notation. Prints the number in scientific notation using the letter ‘e’ to indicate the exponent.                                                                                                                                                                  |\n| `'E'`  | Exponent notation. Same as 'e' except it uses an upper-case 'E' as the separator character.                                                                                                                                                                                 |\n| `'f'`  | Fixed point. Displays the number as a fixed-point number.                                                                                                                                                                                                                   |\n| `'F'`  | Fixed point. Same as 'f', but converts nan to NAN and inf to INF.                                                                                                                                                                                                           |\n| `'g'`  | General format. For a given precision p \u003e= 1, this rounds the number to p significant digits and then formats the result in either fixed-point format or in scientific notation, depending on its magnitude. A precision of 0 is treated as equivalent to a precision of 1. |\n| `'G'`  | General format. Same as 'g' except switches to 'E' if the number gets too large. The representations of infinity and NaN are uppercased, too.                                                                                                                               |\n\n\n#### Formatting library\n\nUses the [STL formatting](https://en.cppreference.com/w/cpp/utility/format) library to\nprint values. A type `T` is eligible to this strategy if there exist a struct\nspecialization\n[`std::formatter\u003cT\u003e`](https://en.cppreference.com/w/cpp/utility/format/formatter).\n\nWithin the *baseline strategies* the [{fmt}](#fmt-1) has precedence over [Formatting\nlibrary](#formatting-library), so if a type is supported by both, the *{fmt}* will be used\ninstead.\n\n##### Formatting library format string\n\nThe format string is forwarded to the *STL Formatting* library. Its syntax can be checked\n[here](https://en.cppreference.com/w/cpp/utility/format/spec).\n\n\n#### {fmt}\n\nUses the [{fmt}](https://fmt.dev) library to print values. A type `T` is eligible to this\nstrategy if there exist either a struct specialization\n[`fmt::formatter\u003cT\u003e`](https://fmt.dev/latest/api/#formatting-user-defined-types) or a\nfunction overload [`auto\nformat_as(T)`](https://fmt.dev/latest/api/#formatting-user-defined-types).\n\n{fmt} is a third-party library, so it needs to be available at the system and enabled to\nbe supported by Icecream-cpp. An explanation of this is in the [*third-party\nlibraries*](#third-party-libraries) section.\n\n##### {fmt} format string\n\nThe format string is forwarded to the *{fmt}* library. Its syntax can be checked\n[here](https://fmt.dev/11.0/syntax/#format-specification-mini-language).\n\n\n#### Characters\n\nAll character types: `char`, `wchar_t`, `char8_t`, `char16_t`, and `char32_t`. This\nstrategy will [transcode](#character-encoding) any other character type to a `char`, using\neither [`wide_string_transcoder`](#wide_string_transcoder) or\n[`unicode_transcoder`](#unicode_transcoder) as appropriated, and after that it will\ndelegate the actual printing to the [IOStreams](#iostreams) strategy.\n\nThis strategy has higher precedence than all the *baseline strategies*.\n\n##### Characters format string\n\nThe same as the [IOStreams format string](#iostreams-format-string).\n\n#### Strings\n\nC strings ([with some subtleties](#c-strings)), [STL's\nstrings](https://en.cppreference.com/w/cpp/string/basic_string), and [STL's\nstring_views](https://en.cppreference.com/w/cpp/string/basic_string_view). These three\nclasses instantiated to all character types: `char`, `wchar_t`, `char8_t`, `char16_t`, and\n`char32_t`.\n\nThis strategy will [transcode](#character-encoding) any other character type to a `char`,\nusing either [`wide_string_transcoder`](#wide_string_transcoder) or\n[`unicode_transcoder`](#unicode_transcoder) as appropriated, and after that it will\ndelegate the actual printing to the [IOStreams](#iostreams) strategy.\n\nThis strategy has higher precedence than all the \"baseline strategies\".\n\n##### Strings format string\n\nThe same as the [IOStreams format string](#iostreams-format-string).\n\n#### Pointer like types\n\nThe `std::unique_ptr\u003cT\u003e` (before C++20) and `boost::scoped_ptr\u003cT\u003e` types will be printed\nlike usual raw pointers.\n\nThe `std::weak_ptr\u003cT\u003e` and `boost::weak_ptr\u003cT\u003e` types will print their address if they are\nvalid or \"expired\" otherwise. The code:\n\n```C++\nauto v0 = std::make_shared\u003cint\u003e(7);\nauto v1 = std::weak_ptr\u003cint\u003e {v0};\n\nIC(v1);\nv0.reset();\nIC(v1);\n```\n\nwill print:\n\n```\nic| v1: 0x55bcbd840ec0\nic| v1: expired\n```\n\nThis strategy has a lower precedence than the *baseline strategies*. So if the printing\ntype is supported by any one of them it will used instead.\n\n#### Range types\n\nConcisely, a range is any object of a type `R`, that holds a collection of elements and is\nable to provide a [`begin`, `end`) pair, where `begin` is an iterator of type `I` and\n`end` is a sentinel of type `S`. The iterator `I` is used to traverse the elements of `R`,\nand the sentinel `S` is used to signal the end of the range interval, it may or may not be\nthe same type as `I`. In precise terms, the Icecream-cpp library is able to format a range\ntype `R` if it fulfills the\n[`forward_range`](https://en.cppreference.com/w/cpp/ranges/forward_range) concept.\n\nIf a type `R` fulfills the range requirements and its elements are *formattable* by\nIceCream, the type `R` is formattable by the range types strategy.\n\nThe code:\n\n```C++\nauto v0 = std::list\u003cint\u003e{10, 20, 30};\nIC(v0);\n```\n\nwill print:\n\n    ic| v0: [10, 20, 30]\n\nA `view` is close concept to `ranges`. Refer to the [*range views\npipeline*](#range-views-pipeline) section to see how to print them.\n\nThis strategy has a higher precedence than the *baseline strategies*, so if the printing\nof a type is supported by both, this strategy will be used instead. This precedence can be\ndisabled by the [force_range_strategy](#force_range_strategy) configuration.\n\n##### Range format string\n\nThe accepted formatting string to a range type is a combination of both a range formatting\nand its elements formatting. The range formatting is syntactically and semantically almost\nidentical to the [Python\nslicing](https://docs.python.org/3/reference/expressions.html#slicings).\n\nFormally, the accepted range types formatting string is:\n\n```\nformat_spec  ::=  [range_fmt][\":\"elements_fmt]\nrange_fmt    ::=  \"[\" slicing | index \"]\"\nslicing      ::=  [lower_bound] \":\" [upper_bound] [ \":\" [stride] ]\nlower_bound  ::=  integer\nupper_bound  ::=  integer\nstride       ::=  integer\nindex        ::=  integer\ninteger      ::=  [\"-\"]digit+\ndigit        ::=  \"0\"...\"9\"\n```\n\nThe same `elements_fmt` string will be used by all the printing elements, so it will have\nthe same syntax as the formatting string of the range elements.\n\nThe code:\n\n```C++\nauto arr = std::vector\u003cint\u003e{10, 11, 12, 13, 14, 15};\nIC_F(\"[:2:-1]:#x\", arr);\n```\nwill print:\n\n    ic| arr: [:2:-1]-\u003e[0xf, 0xe, 0xd]\n\nEven though the specification says that `lower_bound`, `upper_bound`, `stride`, and\n`index`, can have any integer value, some `range` capabilities can restrict them to just\npositive values.\n\nIf a `range` is not [`sized`](https://en.cppreference.com/w/cpp/ranges/sized_range), the\n`lower_bound`, `upper_bound`, and `index` values must be positive. Similarly, if a `range`\nis not [`bidirectional`](https://en.cppreference.com/w/cpp/ranges/bidirectional_range) the\n`stride` value must be positive too.\n\nWhen printing within a [range views pipeline](#range-views-pipeline) using the `IC_FV`\nfunction, all the `lower_bound`, `upper_bound`, and `index` values must be positive.\n\n\n#### Tuple like types\n\nA `std::pair\u003cT1, T2\u003e` or `std::tuple\u003cTs...\u003e` typed variables will print all of its\nelements.\n\nThe code:\n\n```C++\nauto v0 = std::make_pair(10, 3.14);\nauto v1 = std::make_tuple(7, 6.28, \"bla\");\nIC(v0, v1);\n```\n\nwill print:\n\n```\nic| v0: (10, 3.14), v1: (7, 6.28, \"bla\")\n```\n\nThis strategy has a higher precedence than the *baseline strategies*, so if the printing\nof a type supported by both, this strategy will be used instead. This precedence can be\ndisabled by the [`force_tuple_strategy`](#force_tuple_strategy) configuration.\n\n##### Tuple like format string\n\nThe tuple like formatting specification is based on the syntax suggested in the\n[Formatting\nRanges](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2286r8.html#pair-and-tuple-specifiers)\npaper. Since that part hasn't done to the proposal, with any future change we may revisit\nit here too.\n\n```\ntuple_spec  ::= [casing][content]\ncasing      ::= \"n\" | \"m\"\ncontent     ::= (delimiter element_fmt){N}\ndelimiter   ::= \u003ca character, the same to all N expansions\u003e\nelement_fmt ::= \u003cformat specification of the element type\u003e\n```\n\nWhere the number `N` of repetitions in `content` rule is the tuple size.\n\nThe code:\n\n```C++\nauto v0 = std::make_tuple(20, \"foo\", 0.0123);\nIC_F(\"n|#x||.3e\", v0);\n```\n\nwill print:\n\n```\nic| v0: 0x14, \"foo\", 1.230e-02\n```\n\n###### casing\n\nControls the tuple enclosing characters and separator. If not used, the default behavior\nis to enclose the values between `\"(\"` and `\")\"`, and separated by `\", \"`.\n\nIf `n` is used, the tuple won't be enclosed by parentheses. If `m` is used the tuple will\nbe printed \"map value like\", i.e.: not enclosed by parentheses and using `\": \"` as\nseparator. The `m` specifier is valid just to a *pair* or *2-tuple*.\n\n###### content\n\nThe formatting string of each tuple element, separated by a `delimiter` character. This\ncan be any character, which value will be defined by the fist read `char` when parsing\nthis rule.\n\nEach `element_fmt` string will be forwarded to the respective tuple element when printing\nit, so it must follow the formatting specification of that particular element type.\n\n\n#### Optional types\n\nA `std::optional\u003cT\u003e` typed variable will print its value, if it has one, or *nullopt*\notherwise.\n\nThe code:\n\n```C++\nauto v0 = std::optional\u003cint\u003e {10};\nauto v1 = std::optional\u003cint\u003e {};\nIC(v0, v1);\n```\n\nwill print:\n\n```\nic| v0: 10, v1: nullopt\n```\n\nThis strategy has a lower precedence than the *baseline strategies*. So if the printing\ntype is supported by any one of them it will used instead.\n\n##### Optional types format string\n\n```\noptional_spec ::= [\":\"element_fmt]\nelement_fmt   ::= \u003cformat specification of the element type\u003e\n```\n\n```C++\nauto v0 = std::optional\u003cint\u003e{50};\nIC_F(\":#x\", v0);\n```\n\nwill print:\n\n```\nic| v0: 0x32\n```\n\n#### Variant types\n\nA `std::variant\u003cTs...\u003e` or `boost::variant2::variant\u003cTs...\u003e` typed variable will print its\nvalue.\n\nThe code:\n\n```C++\nauto v0 = std::variant\u003cint, double, char\u003e {4.2};\nIC(v0);\n```\n\nwill print:\n\n```\nic| v0: 4.2\n```\n\nThis strategy has a higher precedence than the *baseline strategies*, so if the printing\nof a type supported by both, this strategy will be used instead. This precedence can be\ndisabled by the [force_variant_strategy](#force_variant_strategy) configuration.\n\n##### Variant types format string\n\n```\nvariant_spec ::= [content]\ncontent      ::= (delimiter element_fmt){N}\ndelimiter    ::= \u003ca character, the same to all N expansions\u003e\nelement_fmt  ::= \u003cformat specification of the element type\u003e\n```\n\nWhere the number `N` of repetitions in `content` rule is the number of types in the\nvariant.\n\nThe code:\n\n```C++\nauto v0 = std::variant\u003cint, char const*\u003e{50};\nIC_F(\"|b|s\", v0);\n```\n\nwill print:\n\n```\nic| v0: 110010\n```\n\n\n#### Exception types\n\nTypes inheriting from `std::exception` will print the return of `std::exception::what()`\nmethod. If beyond that it inherits from `boost::exception` too, the response of\n`boost::diagnostic_information()` will be also printed.\n\nThe code:\n\n```C++\nauto v0 = std::runtime_error(\"error description\");\nIC(v0);\n```\n\nwill print:\n\n```\nic| v0: error description\n```\n\nThis strategy has a lower precedence than the *baseline strategies*. So if the printing\ntype is supported by any one of them it will used instead.\n\n#### Clang dump struct\n\nIf using Clang \u003e= 15, a class will be printable even without support to any of the\n*baseline strategies*.\n\nThe code:\n```C++\nclass S\n{\npublic:\n    float f;\n    int ii[3];\n};\n\nS s = {3.14, {1,2,3}};\nIC(s);\n```\nwill print:\n```\nic| s: {f: 3.14, ii: [1, 2, 3]}\n```\n\nThis strategy has a lowest precedence of all the printing strategies. So if the printing\ntype is supported by any one of them it will used instead.\n\n### Third-party libraries\n\nThe Icecream-cpp doesn't have any required dependency on external libraries besides the\nC++ standard library. However, it optionally supports printing some types of\n[Boost](https://www.boost.org/) and [Range-v3](https://ericniebler.github.io/range-v3/)\nlibraries, as well as using the [{fmt}](https://fmt.dev) library alongside the STL's\n[IOStreams](https://en.cppreference.com/w/cpp/io#Stream-based_I.2FO) and\n[formatting](https://en.cppreference.com/w/cpp/utility/format) libraries.\n\nNone of these external libraries are necessary for Icecream-cpp to work properly, and no\naction is required if anyone of them is not available.\n\n#### Boost\n\nAll of the supported Boost types are forward declared in Icecream-cpp header, so you can\nprint them with no further work, just like all the STL types.\n\n\n#### Range-v3\n\nIcecream-cpp can optionally support the printing of Range-v3 views, at any point of a\npipeline flow. This functionality is fully described at the [\"range views\npipeline\"](#range-views-pipeline) section.\n\nThe support to printing Range-v3 types can be explicitly enabled by defining the\n`ICECREAM_RANGE_V3` macro before the `icecream.hpp` header inclusion. That will work\neither by explicitly defining it:\n\n```C++\n#define ICECREAM_RANGE_V3\n#include \"icecream.hpp\"\n```\n\nor by using a compiler command line argument if available. In GCC for example:\n\n```Shell\ngcc -DICECREAM_RANGE_V3 source.cpp\n```\n\nEven when not explicitly defining the `ICECREAM_RANGE_V3` macro, if the inclusion of the\n`icecream.hpp` header is placed some lines below the inclusion of any Range-v3 header, it\nwill be automatically detected and the support enabled. So, a code like this will work\nfine too:\n\n```C++\n#include \u003crange/v3/view/transform.hpp\u003e\n#include \"icecream.hpp\"\n```\n\n#### {fmt}\n\nIcecream-cpp can optionally use the {fmt} library to get the string representation of a\ntype. When available, the {fmt} library will take precedence over the STL's formatting and\nIOStreams libraries. A thorough explanation on this can be seen in the [\"baseline\nprintable types\"](#baseline-printable-types) section.\n\nThe support to the {fmt} library can be explicitly enabled by defining the `ICECREAM_FMT`\nmacro before the `icecream.hpp` header inclusion. That will work either by explicitly\ndefining it:\n\n```C++\n#define ICECREAM_FMT\n#include \"icecream.hpp\"\n```\n\nor by using a compiler command line argument if available. In GCC for example:\n\n```Shell\ngcc -DICECREAM_FMT source.cpp\n```\n\nEven when not explicitly defining the `ICECREAM_FMT` macro, if the inclusion of the\n`icecream.hpp` header is placed some lines below the inclusion of any {fmt} header, it\nwill be automatically detected and the support enabled. So, a code like this will work\nfine too:\n\n```C++\n#include \u003cfmt/format.h\u003e\n#include \"icecream.hpp\"\n```\n\n## Pitfalls\n\n`IC(...)` is a preprocessor macro, it can cause conflicts if there is some\nother `IC` identifier on code. To change the `IC(...)` macro to a longer `ICECREAM(...)`\none, just define `ICECREAM_LONG_NAME` before the inclusion of `icecream.hpp` header:\n\n```C++\n#define ICECREAM_LONG_NAME\n#include \"icecream.hpp\"\n```\n\nWhile most compilers will work just fine, until C++20 the standard requires at least one\nargument when calling a variadic macro. To handle this the nullary macros `IC0()` and\n`ICECREAM0()` are defined alongside `IC(...)` and `ICECREAM(...)`. For the same reason the\npair `IC_A0(callable)` and `ICECREAM_A0(callable)` is defined alongside `IC_A(...)` and\n`ICECREAM_A(...)`.\n\n\n[CI.badge]: https://github.com/renatoGarcia/icecream-cpp/workflows/CI/badge.svg\n[CI.link]: https://github.com/renatoGarcia/icecream-cpp/actions?query=workflow%3ACI\n\n[LICENSE.badge]: https://img.shields.io/badge/licence-MIT-blue\n[LICENSE.link]: https://raw.githubusercontent.com/renatoGarcia/icecream-cpp/master/LICENSE.txt\n","funding_links":[],"categories":["Debug","排序","Recently Updated"],"sub_categories":["多项混杂","[Who Wants to Be a Millionare](https://www.boardgamecapital.com/who-wants-to-be-a-millionaire-rules.htm)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FrenatoGarcia%2Ficecream-cpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FrenatoGarcia%2Ficecream-cpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FrenatoGarcia%2Ficecream-cpp/lists"}