Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
I

icecream-cpp

> 编程语言
Open source

Never use cout/printf to debug again

748 stars0 likes0 views
WebsiteGitHub

About

Never use cout/printf to debug again

Icecream-cpp

[![CI.badge]][CI.link] [![LICENSE.badge]][LICENSE.link]

Icecream-cpp is a little (single header) library to help with print debugging in C++11 and later.

Try it at Compiler Explorer!

Tracking execution flow:

cpp
auto roll_attack(int roll) -> void
{
    std::cout  void
{
    IC();
    if (roll == 20)
        IC();
    else
        IC();
}

and will print something like:

ic| combat.cpp:12 in "void roll_attack(int)"
ic| combat.cpp:14 in "void roll_attack(int)"

Inspecting variables:

cpp
std::cout {U'α', U'β', U'γ', U'δ', U'ε', U'ζ'};
IC_F("[1:-2]", v);

will print:

ic| v: [1:-2]->['β', 'γ', 'δ']

Debugging range pipelines. Insert IC_V() to inspect data flowing through STL ranges or Range-v3 pipelines:

cpp
auto rv = std::vector{1, 0, 2, 3, 0, 4, 5}
    | vws::split(0)
    | IC_V()
    | vws::enumerate;

When iterating on rv, we will see:

ic| range_view_63:16[0]: [1]
ic| range_view_63:16[1]: [2, 3]
ic| range_view_63:16[2]: [4, 5]

This library was inspired by the original Python IceCream library.

Contents

  • Rationale
  • Install
    • Quick Start
  • Basic Usage
    • Direct printing
    • Range views pipeline
    • Return value and Icecream apply macro
    • Output formatting
  • Configuration
  • Printing Strategies
  • Third-party Libraries
  • Compatibility
  • Reference
    • C strings
    • Character Encoding
    • Macro disabling
  • Pitfalls

Rationale

Icecream-cpp is a throwaway library. It is designed for use during development and debugging, not for shipping with the released product. It complements debuggers rather than replacing them, sometimes print debugging is simply faster or more practical.

Because of this, Icecream-cpp is one of the few libraries where you will spend more time writing code with it than reading code with it. Our design goals are expressivity and conciseness: print any variable, format it as desired, with as little code as possible.

Install

Icecream-cpp is a single-file, header-only library with only the STL as dependency. The simplest way to use it is to copy icecream.hpp into your project.

To install system-wide with CMake support, run from the project root:

bash
mkdir build
cd build
cmake ..
cmake --install .

Conan

Released versions are available on Conan. See example_project/conanfile.txt for a usage example.

Nix

Icecream-cpp can be included as a flakes input:

nix
inputs.icecream-cpp.url = "github:renatoGarcia/icecream-cpp";

The package can be referenced directly:

nix
inputs.icecream-cpp.packages.${system}.default

Alternatively, the flake provides an overlay for use when importing nixpkgs:

nix
import nixpkgs {
  system = "x86_64-linux";
  overlays = [
    icecream-cpp.overlays.default
  ];
}

This adds an icecream-cpp derivation to the nixpkgs attribute set.

See example_project/flake.nix for a working example.

Quick Start

With CMake:

cmake
find_package(icecream-cpp)
target_link_libraries( PRIVATE icecream-cpp::icecream-cpp)

Where `` is the executable or library being compiled.

Then include the header:

cpp
#include 

int main(int, char*[])
{
    auto x = 42;
    auto name = std::string{"foo"};
    IC(x, name);  // prints: ic| x: 42, name: "foo"
    return 0;
}

Basic Usage

The main Icecream-cpp functions are IC, IC_A, and IC_V; together with their respective counterparts IC_F, IC_FA, and IC_FV; that behave the same but accept an output formatting string as their first argument.

Direct printing

IC is the simplest of the Icecream-cpp functions. If called with no arguments it will print the prefix, the source file name, the current line number, and the current function signature. The code:

cpp
auto my_function(int foo, double bar) -> void
{
    // ...
    IC();
    // ...
}

will print:

ic| test.cpp:34 in "void my_function(int, double)"

If called with arguments it will print the prefix, those argument names, and their values. The code:

cpp
auto v0 = std::vector{1, 2, 3};
auto s0 = std::string{"bla"};
IC(v0, s0, 3.14);

will print:

ic| v0: [1, 2, 3], s0: "bla", 3.14: 3.14

The variant IC_F behaves the same as the IC function, but accepts an output formatting string as its first argument.

Range views pipeline

To print the data flowing through a range views pipeline (both with STL ranges and Range-v3), we can use either the IC_V or IC_FV functions, which will lazily print any input coming from the previous view. IC_FV behaves the same as IC_V, but accepts a format string (same syntax as the range format string) as its first argument. Since these functions will be placed within a range views pipeline, the printing will be done lazily, while each element is generated. For instance:

cpp
namespace vws = std::views;
auto v0 = vws::iota('a') | vws::enumerate | IC_V() | vws::take(3);
for (auto e : v0)
{
    //...
}

In this code nothing will be immediately printed when v0 is created, just when iterating over it. At each for loop iteration one line will be printed, until we have the output:

ic| range_view_61:53[0]: (0, 'a')
ic| range_view_61:53[1]: (1, 'b')
ic| range_view_61:53[2]: (2, 'c')

[!NOTE] The Icecream-cpp will enable its support to Range-v3 types either if the "icecream.hpp" header is included some lines after any Range-v3 header, or if the ICECREAM_RANGE_V3 macro was declared before the "icecream.hpp" header inclusion. This is discussed in details at the third-party libraries section.

IC_V has the signature IC_V(name, projection), and IC_FV has IC_FV(fmt, name, projection). In both, the name and projection parameters are optional.

fmt

The same syntax as described at range types format string.

name

The variable name used by the view when printing. The printing layout is: []: . If the name parameter is not used, the default value to `` is range_view_.

The code:

cpp
vws::iota('a') | vws::enumerate | IC_V("foo") | vws::take(2);

when iterated over will print:

ic| foo[0]: (0, 'a')
ic| foo[1]: (1, 'b')

projection

A callable that will receive as input a const reference to the current element at the range views pipeline, and must return the actual value to be printed.

The code:

cpp
vws::iota('a') | vws::enumerate | IC_V([](auto e){return std::get(e);}) | vws::take(2);

when iterated over will print:

ic| range_view_61:53[0]: 'a'
ic| range_view_61:53[1]: 'b'

[!NOTE] The IC_V function will forward to the next view the exact same input element, just as it was received from the previous view. No action done by the projection function will have any effect on that.

Return value and Icecream apply macro

IC (and IC_F) returns no value when called with zero or multiple arguments. When called with exactly one argument, it returns that argument.

This allows using IC to inspect a function argument at the call site without changing the code structure. In the code:

cpp
my_function(IC(MyClass{}));

the MyClass object will be forwarded to my_function as if the IC function wasn't there. The my_function will continue receiving a rvalue reference to a MyClass object.

This approach, however, is not practical when the function has multiple arguments. In the code:

cpp
another_function(IC(a), IC(b), IC(c), IC(d));

besides writing four times the IC function, the printed output will be split in four lines. Something like:

ic| a: 1
ic| b: 2
ic| c: 3
ic| d: 4

To work around that, there is the IC_A function. IC_A behaves exactly like the IC function, but receives a callable as its first argument, and will call it using the remaining arguments, printing all of them before that. That previous example code could be rewritten as:

cpp
IC_A(another_function, a, b, c, d);

and this time it will print:

ic| a: 1, b: 2, c: 3, d: 4

The IC_A function will return the same value returned by the callable. The code:

cpp
auto mc = std::make_unique();
auto r = IC_A(mc->my_function, a, b);

behaves exactly the same as:

cpp
auto mc = std::make_unique();
auto r = mc->my_function(a, b);

but will print the values of a and b.

The IC_FA variant does the same as the IC_A function, but accepts an output formatting string as its first argument, just before the callable argument.

Output formatting

It is possible to configure how a value must be formatted while printing. The following code:

cpp
auto a = int{42};
auto b = int{20};
IC_F("#X", a, b);

will print:

ic| a: 0X2A, b: 0X14

if using the IC_F variant instead of the plain IC function. A similar result would be achieved if using IC_FA and IC_FV in place of IC_A and IC_V respectively.

When using the formatting function variants (IC_F and IC_FA), the same formatting string will be applied by default to all of its arguments. That could be a problem if we need to have distinct formatting to each argument, or if the arguments have multiple types with mutually incompatible format syntaxes. Therefore, to set a distinct formatting string to a specific argument we can wrap it with the IC_ function. The code:

cpp
auto a = int{42};
auto b = int{20};
IC_F("#X", a, IC_("d", b));

will print:

ic| a: 0X2A, b: 20

The IC_ function can also be used within the plain IC (or IC_A) function:

cpp
auto a = int{42};
auto b = int{20};
IC(IC_("#x", a), b);

will print:

ic| a: 0x2a, b: 20

The last argument in an IC_ function call is the one that will be printed, all other arguments coming before the last will be converted to a string using the to_string function and concatenated to the resulting formatting string.

cpp
auto a = float{1.234};
auto width = int{7};
IC(IC_("* void
{
    IC_CONFIG.line_wrap_width(20);

    IC_CONFIG_SCOPE();
    IC_CONFIG.context_delimiter("|");
    IC_CONFIG.show_c_string(true);

    {
        IC_CONFIG_SCOPE();
        IC_CONFIG.show_c_string(false);
        // A
    }
    // B
}

At line A, the value of IC_CONFIG's line_wrap_width, context_delimiter, and show_c_string will be respectively: 20, "|", and false.

After the closing of the innermost scope block, at line B, the value of IC_CONFIG's line_wrap_width, context_delimiter, and show_c_string will be respectively: 20, "|", and true.

The reading and writing operations on IC_CONFIG objects are thread safe.

[!NOTE] Any modification in an IC_CONFIG, other than to the global instance, will be seen only within the current scope. As a consequence, those modi

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C++cppcpp11cpp14cpp17

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言