­čŹŽ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/[email protected]

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
  • 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
  • Clang dump struct type info limitation

    Clang dump struct type info limitation

    Due to limited information provided by Clang when using __builtin_dump_struct function, some struct members types can not be printed. Enums, none of them can be printed. With arrays, only the ones with elements of fundamental types, fixed width integers, std::size_t, std::ptrdiff_t, and any pointer, declared using canonical names (i.e.: not type aliases), can be printed.

    When using the __builtin_dump_struct function, both enums and arrays are dumped to some_printf_func using the %p specifier, regardless of its elements type. With only that information would not be possible recover and print neither the enum values nor any array element.

    However, preceding each call to some_printf_func with a %p specifier by __builtin_dump_struct, a call to that same function will be done having as argument a string with the member signature (type and name). To enums that is not of any help, but to arrays, inspecting that string is possible deduce the array size and its elements types.

    Knowing that information, an array dumped with a preceding "short [50] bla" string can be deduced as being an array of 50 short int. That works identically for any fundamental type, but is not possible infer the array elements type if an alias is used. With the code:

    using Integer = int;
    
    struct Point
    {
        double x;
        double y;
    };
    
    struct S
    {
        Integer integers[10];
        Point coords[5];
    };
    

    there is no way to know what fundamental type Integer is an alias to, neither what are the internal members of Point struct. Consequently, both integers and coords members can not be printed.

    To enums, its values are represented using an integral type defined at enum declaration, like:

    enum EN : std::uint64_t
    {
        UM,
        DOIS,
        TRES
    };
    

    However, inside of some_printf_func it's not possible to get that information. Therefore, not kowning the bit size of each enum value it's not possible print them.

    opened by renatoGarcia 0
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 31 Sep 21, 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 264 Sep 7, 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 512 Sep 15, 2022
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.2k Sep 21, 2022
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 Sep 21, 2022
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 15.6k Sep 22, 2022
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 15.6k Sep 22, 2022
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 6.8k Sep 19, 2022
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 717 Sep 19, 2022
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 Jan 21, 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 11.7k Sep 24, 2022
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 Sep 18, 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 27.6k Sep 22, 2022
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 95 Sep 18, 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 171 Sep 18, 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 103 Sep 7, 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 1.8k Sep 18, 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 Sep 16, 2022