🍦IceCream-Cpp is a little (single header) library to help with the print debugging on C++11 and forward.

Overview

IceCream-Cpp

CI.badge LICENSE.badge

IceCream-Cpp is a little (single header) library to help with the print debugging on C++11 and forward.

Try it at godbolt!

Contents

With IceCream-Cpp, an execution inspection:

auto my_function(int i, double d) -> void
{
    std::cout << "1" << std::endl;
    if (condition)
        std::cout << "2" << std::endl;
    else
        std::cout << "3" << std::endl;
}

can be coded instead:

auto my_function(int i, double d) -> void
{
    IC();
    if (condition)
        IC();
    else
        IC();
}

and will print something like:

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

Also, any variable inspection like:

std::cout << "a: " << a
          << ", b: " << b
          << ", sum(a, b): " << sum(a, b)
          << std::endl;

can be simplified to:

IC(a, b, (sum(a, b)));

and will print:

ic| a: 7, b: 2, (sum(a, b)): 9

This library is inspired by and aims to behave the most identical as possible to the original Python IceCream library.

Install

The IceCream-Cpp is a one file, header only library, having the STL as its only dependency. The most direct way to install it is just copy the icecream.hpp header to inside your project.

To properly install it system wide, together with the CMake project files, run on IceCream-Cpp project root directory:

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

Nix

If using Nix, any committed version on master branch can be installed using the archive https://github.com/renatoGarcia/icecream-cpp/archive/.tar.gz, where .tar.gz could be any tag or commit hash of master branch.

For instance, to install the master HEAD commit, environment wide:

nix-env -if https://github.com/renatoGarcia/icecream-cpp/archive/master.tar.gz

To use a specific commit in a shell.nix:

icecream-cpp = pkgs.callPackage (
  fetchTarball https://github.com/renatoGarcia/icecream-cpp/archive/.tar.gz
) { inherit pkgs; };

where pkgs is the variable with the evaluated nixpkgs.

Conan

The released versions are available on Conan too:

conan install icecream-cpp/0.3.1@

Usage

If using CMake:

find_package(IcecreamCpp)
include_directories(${IcecreamCpp_INCLUDE_DIRS})

will add the installed directory on include paths list.

After including the icecream.hpp header on a source file, here named test.cpp:

#include <icecream.hpp>

A macro IC(...) will be defined. If called with no arguments it will print the prefix (default ic| ), the source file name, the current line number, and the current function signature. The code:

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 arguments names, and its values. The code:

auto v0 = std::vector<int> {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

Return value

If called with no arguments the IC(...) macro will return void, if called with one argument it will return the argument itself, and if called with multiple arguments it will return a tuple with all of them:

auto a = int {7};
auto b = std::string {"bla"};
auto c = float {3.14};

IC();
int& d = IC(a);
std::tuplefloat&> e = IC(b, c);

Configuration

The Icecream class is internally implemented as a singleton. All the configuration changes will be done to a unique object, and shared across all the program and threads.

All configurations are done/viewed through accessor methods, using the icecream::ic object. To allow the method chaining idiom all the set methods return a reference of the ic object:

icecream::ic
    .prefix("ic: ")
    .show_c_string(false)
    .line_wrap_width(70);

For simplification purposes, on the following examples a using icecream::ic statement will be presumed.

enable/disable

Enable or disable the output of IC(...) macro, enabled default.

  • set:
    auto enable() -> IcecreamAPI&;
    auto disable() -> IcecreamAPI&;

The code:

IC(1);
ic.disable();
IC(2);
ic.enable();
IC(3);

will print:

ic| 1: 1
ic| 3: 3

stream

Warning: this method will return a reference to the internal std::ostream. The operations done on that reference will not be thread safe.

The std::ostream where the output will be streamed.

  • get:
    auto stream() -> std::ostream&;

The default stream buffer associated is the same as std::cerr, but that can be changed. For instance, to stream the output to a string:

auto sstr = std::stringstream {};
ic.stream().rdbuf(sstr.rdbuf());

prefix

The text that will be printed before each output. It can be set to a string, a nullary callable that returns an object having an overload of operator<<(ostream&, T), or any number of instances of those two. The printed prefix will be a concatenation of all those elements.

  • set:
    template <typename... Ts>
    auto prefix(Ts&& ...values) -> IcecreamAPI&;

The code:

ic.prefix("icecream| ");
IC(1);
ic.prefix([]{return 42;}, "- ");
IC(2);
ic.prefix("thread ", std::this_thread::get_id, " | ");
IC(3);

will print:

icecream| 1: 1
42- 2: 2
thread 1 | 3: 3

show_c_string

Controls if a char* variable should be interpreted as a null-terminated C string (true) or a pointer to a char (false). The default value is true.

  • get:
    auto show_c_string() const -> bool;
  • set:
    auto show_c_string(bool value) -> IcecreamAPI&;

The code:

char const* flavor = "mango";

ic.show_c_string(true);
IC(flavor);

ic.show_c_string(false);
IC(flavor);

will print:

ic| flavor: "mango";
ic| flavor: 0x55587b6f5410

line_wrap_width

The maximum number of characters before the output be broken on multiple lines. Default value of 70.

  • get:
    auto line_wrap_width() const -> std::size_t;
  • set:
    auto line_wrap_width(std::size_t value) -> IcecreamAPI&;

include_context

If the context (source name, line number, and function name) should be printed even when printing variables. Default value is false.

  • get:
    auto include_context() const -> bool;
  • set:
    auto include_context(bool value) -> IcecreamAPI&;

context_delimiter

The string separating the context text from the variables values. Default value is "- ".

  • get:
    auto context_delimiter() const -> std::string;
  • set:
    auto context_delimiter(std::string const& value) -> IcecreamAPI&;

Printing logic

When printing a type T, the precedence is use an overloaded function operator<<(ostream&, T) always when it is available. The exceptions to that rule are strings (C strings, std::string, and std::string_view), char and bounded arrays. Strings will be enclosed by ", char will be enclosed by ', and arrays are considered iterables rather than let decay to raw pointers.

In general, if an overload of operator<<(ostream&, T) is not available to a type T, a call to IC(t) will result on a compiling error. All exceptions to that rule, when IceCream-Cpp will print a type T even without a operator<< overload are discussed below. Note however that even to those, if a user implements a custom operator<<(ostream&, T) that will take precedence and used instead.

C strings

C strings are ambiguous. Should a char* foo variable be interpreted as a pointer to a single char or as a null-terminated string? Likewise, is the char bar[] variable an array of single characters or a null-terminated string? Is char baz[3] an array with three single characters or is it a string of size two plus a '\0'?

Each one of those interpretations of foo, bar, and baz would be printed in a distinct way. To the code:

char flavor[] = "pistachio";
IC(flavor);

all three outputs below are correct, each one having a distinct interpretation of what should be the flavor variable.

ic| flavor: 0x55587b6f5410
ic| flavor: ['p', 'i', 's', 't', 'a', 'c', 'h', 'i', 'o', '\0']
ic| flavor: "pistachio"

The IceCream-Cpp policy is handle any bounded char array (i.e.: array with a known size) as an array of single characters. So the code:

char flavor[] = "chocolate";
IC(flavor);

will print:

ic| flavor: ['c', 'h', 'o', 'c', 'o', 'l', 'a', 't', 'e', '\0']

unbounded char[] arrays (i.e.: array with an unknown size) will decay to char* pointers, and will be printed either as a string or a pointer as configured by the show_c_string option.

Pointer like types

The std::unique_ptr (before C++20) and boost::scoped_ptr types will be printed like usual raw pointers.

The std::weak_ptr and boost::weak_ptr types will print their address if they are valid or "expired" otherwise. The code:

auto v0 = std::make_shared<int>(7);
auto v1 = std::weak_ptr<int> {v0};

IC(v1);
v0.reset();
IC(v1);

will print:

ic| v1: 0x55bcbd840ec0
ic| v1: expired

Iterable types

If for a type A with an instance a, all following operations are valid:

auto it = begin(a);
it != end(a);
++it;
*it;

the type A is defined iterable, and if A has no overload of operator<<(ostream&, A), all of its items will be printed instead. The code:

auto v0 = std::list<int> {10, 20, 30};
IC(v0);

will print:

ic| v0: [10, 20, 30]

Tuple like types

A std::pair or std::tuple typed variables will print all of its elements.

The code:

auto v0 = std::make_pair(10, 3.14);
auto v1 = std::make_tuple(7, 6.28, "bla");
IC(v0, v1);

will print:

ic| v0: (10, 3.14), v1: (7, 6.28, "bla")

Optional types

A std::optional typed variable will print its value, if it has one, or nullopt otherwise.

The code:

auto v0 = std::optional<int> {10};
auto v1 = std::optional<int> {};
IC(v0, v1);

will print:

ic| v0: 10, v1: nullopt

Variant types

A std::variant or boost::variant2::variant typed variable will print its value.

The code:

auto v0 = std::variant<int, double, char> {4.2};
IC(v0);

will print:

ic| v0: 4.2

Exception types

Types inheriting from std::exception will print the return of std::exception::what() method. If beyond that it inherits from boost::exception too, the response of boost::diagnostic_information() will be also printed.

The code:

auto v0 = std::runtime_error("error description");
IC(v0);

will print:

ic| v0: error description

Standard layout types (Clang only)

With some exceptions (see issue #7), if using Clang >= 7, any standard layout type (C compatible structs roughly speaking) is printable even without an operator<<(ostream&, T) overload.

The code:

class S
{
public:
    float f;
    int ii[3];
};

S s = {3.14, {1,2,3}};
IC(s);

will print:

ic| s: {f: 3.14, ii: [1, 2, 3]}

Pitfalls

The IC(...) is a preprocessor macro, then care must be taken when using arguments with commas. Any argument having commas must be enclosed by parenthesis. The code:

auto sum(int i0, int i1) -> int
{
    return i0 + i1;
}

// ...

IC((sum(40, 2)));

will work and print something like:

ic| (sum(40, 2)): 42

Also, since IC(...) is a preprocessor macro, it can cause conflicts if there is some other IC identifier on code. To change the IC(...) macro to a longer ICECREAM(...) one, just define ICECREAM_LONG_NAME before the inclusion of icecream.hpp header:

#define ICECREAM_LONG_NAME
#include "icecream.hpp"

While most compilers will work just fine, until the C++20 the standard requires at least one argument when calling a variadic macro. To handle this the nullary macros IC0() and ICECREAM0() are defined alongside IC(...) and ICECREAM(...).

Similar projects

The CleanType library has a focus on printing readable types names, but there is support to print variables names and values alongside its types.

Comments
  • Simplest way to print function called with the arguments that were passed

    Simplest way to print function called with the arguments that were passed

    What is the simplest way to print function called with the arguments that were passed? I'm debugging an interface implementation, so I don't have so much control over who is calling me methods; rather I am on the receiving end of them being called.

    I can add IC() at the beginning of a function to see its name and arguments, but the argument values themselves aren't printed.

    opened by vadi2 6
  • avoid using the _() macro/function which used for text translation in other libraries

    avoid using the _() macro/function which used for text translation in other libraries

    Hi, I try to use the header file, but it reported a build error.

    I see that in some of my header file, the _() is defined to text translation. (For example, under wxWidgets, the _() is defined as:

     #define _(s)                               wxGetTranslation((s))
    

    While, I see in the icecream.hpp, it also define some function like:

        template<typename... Ts>
        auto _(std::string const& fmt, Ts&&... vs) -> detail::FormatterPack<decltype(std::forward<Ts>(vs))...>
        {
            return detail::FormatterPack<decltype(std::forward<Ts>(vs))...>{fmt, std::forward<Ts>(vs)...};
        }
    

    So, I would like to find a way to solve such conflict issue. Thanks.

    opened by asmwarrior 2
  • containers are not properly displayed when they are members of a user-defined class

    containers are not properly displayed when they are members of a user-defined class

    I'm trying to print an instance of a user-defined class as follows.

      class MyStruct {
        public:
        int i;
        double d;
        std::string s;
        std::vector<int> v;
      };
    
      MyStruct x;
      x.i = 2; x.d = 0.9; x.s = std::string("abc"); x.v = {1, 2, 3};     // set member variables
      IC(x);
    

    What I expected was something like

    ic| x: {i: 2, d: 0.9, s: "abc", v: [1, 2, 3]}
    

    but what I actually got was the following.

    ic| x: {i: 2, d: 0.9, s: {__r_: {}}, v: {}}
    

    It seems that a member variable is not properly displayed when it is an STL container. If I print each member IC(x.s, x.v), I got the correct output. Could someone investigate this issue?

    FYI, my compiler is Apple clang version 11.0.0 (clang-1100.0.33.17).

    opened by yohm 2
  • Printing a std::string_view relies on a terminating null-byte

    Printing a std::string_view relies on a terminating null-byte

    std::string str = "ABCDE";
    std::string_view sub{str.data() + 1, 3};
    IC(sub); 
        // expected: ic| sub: "BCD"
        // actual:   ic| sub: "BCDE"
    

    (see https://godbolt.org/z/b3a9Eq)

    I think line 703

    buf << '"' << cv.to_bytes(value.data()) << '"';
    

    should be

    buf << '"' << cv.to_bytes(value.data(), value.data() + value.size()) << '"';
    

    and maybe the Microsoft-specific code a few lines above has to be adjusted, too.
    But I don't know icecream-cpp well enough to be sure.

    opened by siebenschlaefer 1
  • Linking errors

    Linking errors

    I was using version 0.1.0 of icecream-cpp for a while, and I have IC statements in multiple source files, which are compiled independently (as cmake object libraries) and then linked together into different binaries and shared libraries. This used to work very nicely. Now I upgraded to 0.3.0, recompiled everything, and get linking errors like so:

    multiple definition of `icecream::detail::show_c_string()'
    

    I know that this is potentially an issue with header only libraries in general, but perhaps there is something you can do, as it used to work in previous versions.

    opened by mrjackbo 1
  • Fix wide characters support.

    Fix wide characters support.

    Add tests to wide characters. Fix bug on MacOS when converting between same character types. Fix bug when printing string_view's without a terminating null-byte.

    fix #13

    opened by renatoGarcia 0
  • Print wrong variable name when casting

    Print wrong variable name when casting

    The code

    char const foo[] = "bar";
    IC((char*)foo);
    

    Should print

    ic| (char*)foo: "bar"
    

    But now is printing

    ic| char*: "bar"
    
    opened by renatoGarcia 0
  • cannot display char as integer

    cannot display char as integer

    CleanShot 2022-09-03 at 10 51 31@2x

    #include <algorithm>
    #include <numeric> 
    
    #include "icecream.hpp"
    
    using namespace std;
    
    #define DETECT_TYPE(x)                        \
        do                                        \
        {                                         \
            IC(sizeof(x));                        \
            IC_("d", std::numeric_limits<x>::max());    \
            IC_("d", std::numeric_limits<x>::min());    \
            IC_("d", std::numeric_limits<x>::lowest()); \
        } while (0)
    
    
    int main(int argc, char **argv)
    {
        DETECT_TYPE(char);
        DETECT_TYPE(unsigned char);
        return 0;
    }
    

    expect: it should display max: 128, min: -127, lowest: -127 gcc 11.2 linux

    opened by logerrors 0
  • Release a new version

    Release a new version

    Just creating a git tag, and changing the version number in CMakeLists.txt would be good.

    The last release was 2 years ago, while there has been a bunch of new stuff added!

    opened by dufferzafar 0
  • Comparison with dbg-macro

    Comparison with dbg-macro

    I'm used to the Python icecream library and really like this style of printf based debugging.

    While looking for C++ based library, I found this repo & another one: https://github.com/sharkdp/dbg-macro

    Are you aware of it? Any comparison between the two? which features one supports & the other doesn't etc?

    opened by dufferzafar 5
  • Functionalities in <codecvt> are deprecated since C++ 17/20

    Functionalities in are deprecated since C++ 17/20

    Some functionalities in <codecvt> (e.g. std::codecvt<char16_t, char, std::mbstate_t>) are being deprecated since C++ 17/20. Please refer to https://en.cppreference.com/w/cpp/locale/codecvt for more details. Compiling with these usages may throw annoying warnings.

    opened by LyricZhao 1
Releases(v0.3.1)
Owner
Renato Garcia
Renato Garcia
Windows-only Remote Access Tool (RAT) with anti-debugging and anti-sandbox checks

RATwurst Windows-only Remote Access Tool (RAT) with anti-debugging and anti-sandbox checks. For educational purposes only. The reason behind this proj

AccidentalRebel 35 Dec 5, 2022
Debugging like a sir (in C)

Debugging like a sir (in C) #include "debug.h" int answer(void) { return 42; } int main(void) { int num = 1; char *str = "hello";

Dario Sneidermanis 263 Dec 5, 2022
🧪 single header unit testing framework for C and C++

?? utest.h A simple one header solution to unit testing for C/C++. Usage Just #include "utest.h" in your code! The current supported platforms are Lin

Neil Henning 560 Jan 1, 2023
The fastest feature-rich C++11/14/17/20 single-header testing framework

master branch Windows All dev branch Windows All doctest is a new C++ testing framework but is by far the fastest both in compile times (by orders of

Viktor Kirilov 4.5k Jan 5, 2023
Single C file, Realtime CPU/GPU Profiler with Remote Web Viewer

Remotery A realtime CPU/GPU profiler hosted in a single C file with a viewer that runs in a web browser. Supported Platforms: Windows Windows UWP (Hol

Celtoys 2.4k Jan 8, 2023
A modern, C++-native, header-only, test framework for unit-tests, TDD and BDD - using C++11, C++14, C++17 and later (or C++03 on the Catch1.x branch)

Catch2 v3 is being developed! You are on the devel branch, where the next major version, v3, of Catch2 is being developed. As it is a significant rewo

Catch Org 16k Jan 8, 2023
A modern, C++-native, header-only, test framework for unit-tests, TDD and BDD - using C++11, C++14, C++17 and later (or C++03 on the Catch1.x branch)

Catch2 v3 is being developed! You are on the devel branch, where the next major version, v3, of Catch2 is being developed. As it is a significant rewo

Catch Org 16k Jan 8, 2023
A microbenchmark support library

Benchmark A library to benchmark code snippets, similar to unit tests. Example: #include <benchmark/benchmark.h> static void BM_SomeFunction(benchmar

Google 7.1k Jan 3, 2023
C++ Benchmark Authoring Library/Framework

Celero C++ Benchmarking Library Copyright 2017-2019 John Farrier Apache 2.0 License Community Support A Special Thanks to the following corporations f

John Farrier 728 Jan 6, 2023
DotX64Dbg aims to provide a seamless way to write and test plugins for X64Dbg using .Net 5.0 and C#.

DotX64Dbg (EARLY ALPHA) Plugins and Scripting with C# for x64Dbg. Create Plugins for X64Dbg with ease DotX64Dbg aims to provide a seamless way to writ

ζeh Matt 7 Oct 16, 2022
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.

The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.

null 12.1k Jan 2, 2023
CppUTest unit testing and mocking framework for C/C++

CppUTest CppUTest unit testing and mocking framework for C/C++ More information on the project page Slack channel: Join if link not expired Getting St

CppUTest 1.1k Dec 26, 2022
Googletest - Google Testing and Mocking Framework

GoogleTest OSS Builds Status Announcements Release 1.10.x Release 1.10.x is now available. Coming Soon Post 1.10.x googletest will follow Abseil Live

Google 28.7k Jan 7, 2023
A simple C++ 03/11/etc timer class for ~microsecond-precision cross-platform benchmarking. The implementation is as limited and as simple as possible to create the lowest amount of overhead.

plf_nanotimer A simple C++ 03/11/etc timer class for ~microsecond-precision cross-platform benchmarking. The implementation is as limited and simple a

Matt Bentley 102 Dec 4, 2022
Anti-Debug and Anti-Memory Dump for Android

AntiDebugandMemoryDump Anti-Debug and Anti-Memory Dump for Android Some known techniques for anti-debug and anti-memory dump have been used in this pr

Darvin 184 Dec 25, 2022
An efficient OpenFST-based tool for calculating WER and aligning two transcript sequences.

fstalign Overview Installation Dependencies Build Docker Quickstart WER Subcommand Align Subcommand Inputs Outputs Overview fstalign is a tool for cre

Rev 108 Dec 12, 2022
HyperDbg debugger is an open-source, hypervisor-assisted user-mode, and kernel-mode Windows debugger 🐞

HyperDbg debugger is an open-source, hypervisor-assisted user-mode, and kernel-mode Windows debugger with a focus on using modern hardware technologies. It is a debugger designed for analyzing, fuzzing and reversing. ??

HyperDbg 2k Dec 30, 2022
Watch for file changes and auto restart an application using fork checkpoints to continue the process (for quick live development)

Forkmon Watch for file changes and auto restart an application using fork checkpoints to continue. Intended for quick live development. This works onl

Eduardo Bart 12 Aug 27, 2022
Palanteer is a set of high performance visual profiler, debugger, tests enabler for C++ and Python

Palanteer is a set of lean and efficient tools to improve the general software quality, for C++ and Python programs.

Damien Feneyrou 1.9k Dec 29, 2022