XUtils

cpp-dump

A C++ library for debugging purposes that can print any variable, even user-defined types. [MIT]


cpp-dump

日本語記事はこちら!
I Made a C++ Version of Python print() Function (DEV article)

Overview

cpp-dump is a C++ library for debugging purposes that can print any variable, including user types.

This library has the following features:

  • Prints string representations of a wide variety of types to the standard error output (std::clog). This includes multidimensional arrays, (multi)maps, and (multi)sets, and even complex numbers, error objects, etc.
  • Automatically indents so that the output fits into the maximum line width.
  • Customizable output color.
  • The file name, line, and function name can also be attached to the log output.
  • Header-only library, no build or dependencies required.
  • Can print even user-defined types by using macros or defining operators.
  • The string representation of variables is similar to JavaScript, Python, and C++ syntax.

Introduction

You can print variables of a wide variety of types by passing them to the cpp_dump(expressions...) macro.
See Full Example Code

std::vector<std::vector<int>> my_vector{{3, 5, 8, 9, 7}, {9, 3, 2, 3, 8}};
cpp_dump(my_vector);

introduction.png

Features

Auto indent

cpp-dump automatically indents so that the output does not exceed the maximum width. See Full Example Code

cpp_dump(my_vector);
my_vector.push_back("This is a test string.");
cpp_dump(my_vector);

Auto indent

Filename and line can be printed instead of [dump]

If you want to print the filename and line instead of [dump], use the following code. The cpp_dump() macro will automatically detect and print the filename and the line. You can attach the function name, too. See Customize [dump] for details.
See Full Example Code

// Print the filename and line instead of [dump]
CPP_DUMP_SET_OPTION(log_label_func, cp::log_label::filename());
// Print along with the function name
CPP_DUMP_SET_OPTION(log_label_func, cp::log_label::filename(true));

customize-dump.png

Can print even user-defined types

If you want to print a user-defined type, you can enable the library to print it by using macros or defining an operator. The following is an example of the use of macros. See How to print a user-defined type with cpp-dump for details.
See Full Example Code

CPP_DUMP_DEFINE_DANGEROUS_EXPORT_OBJECT(i, str());

user-defined-class2.png

Advanced Feature

Manipulators to change the display style

Using manipulators, you can set which and how many elements of an array/map/set and how the index of an array and integers will be displayed.
See Formatting with manipulators for details.

manipulators.png

cont-index.png

Requirement

  • C++17 or higher.
  • No build or dependencies are required since cpp-dump is a header-only library.

#include “path/to/cpp-dump/dump.hpp”


## Configuration (as needed)

If you want to customize the library, you can write the configuration code as follows:

```cpp
// You can also write this in a header file -----------------------------------
#ifdef DEBUGGING
#include "path/to/cpp-dump/dump.hpp"
namespace cp = cpp_dump;
CPP_DUMP_SET_OPTION_IN_GLOBAL(max_line_width, 100);
#else
#define cpp_dump(...)
#endif
// You can also write this in a header file -----------------------------------

int main() {
  // To be continued...
}

If you want to configure the library within a function, use CPP_DUMP_SET_OPTION() instead.

// You can also write this in a header file -----------------------------------
#ifdef DEBUGGING
#include "path/to/cpp-dump/dump.hpp"
namespace cp = cpp_dump;
#else
#define cpp_dump(...)
#define CPP_DUMP_SET_OPTION(...)
#endif
// You can also write this in a header file -----------------------------------

int main() {
  CPP_DUMP_SET_OPTION(max_line_width, 100);

  // To be continued...
}

Configuration options

See also Variables.

max_line_width

Type: std::size_t Default: 160
The maximum line width of the strings returned by cpp_dump() and cpp_dump::export_var(), which cpp_dump() internally uses to convert a variable into a string.

max_depth

Type: std::size_t Default: 4
The maximum number of times cpp_dump::export_var() is called recursively.

max_iteration_count

Type: std::size_t Default: 16
The maximum number of iterations of cpp_dump::export_var() over an iterator.
Note that in a single call, cpp_dump::export_var() calls itself at most (max_iteration_count^(max_depth+1)-1)/(max_iteration_count-1)-1 times.

Type: bool Default: true
Whether cpp_dump() prints the expressions.

log_label_func

Type: cpp_dump::log_label::log_label_func_t Default: cpp_dump::log_label::default_func
The function that returns the label that cpp_dump() prints at the beginning of the output.

es_style

Type: enum class cpp_dump::es_style_t Default cpp_dump::es_style_t::original
The style of the escape sequences (the output coloring).

Name Description
original Default. Pointers, bitsets, complexes, and etc. are colored differently from by_syntax
by_syntax Use a color scheme closer to standard syntax highlighting
no_es Turn off output coloring

es_value

Type: cpp_dump::es_value_t Default: (Default constructor, see Types)
The values of the escape sequences.

Macros

/**
 * Output string representations of expression(s) and result(s) to std::clog.
 * This is an alias of CPP_DUMP(expressions...).
 */
#define cpp_dump(expressions...)

/**
 * Output string representations of expression(s) and result(s) to std::clog.
 */
#define CPP_DUMP_DEFINE_EXPORT_ENUM(T, members...)

/**
 * Set a value to a variable in cpp_dump namespace.
 * You can also assign values to the variables directly.
 */
#define CPP_DUMP_SET_OPTION(variable, value)

/**
 * Set a value to a variable in cpp_dump namespace.
 * Use this if you want to run it in the global namespace, meaning before the main starts.
 */
#define CPP_DUMP_SET_OPTION_IN_GLOBAL(variable, value)

Functions

/**
 * Return a string representation of a variable.
 * cpp_dump() uses this function internally.
 */
template <typename T>
std::string cpp_dump::export_var(const T &value);

// Manipulators (See 'Formatting with manipulators' for details.)
cpp_dump::front(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::middle(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::back(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::both_ends(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::int_style(int base = 16, int digits = -1, int chunk = 2, bool space_fill = false);
cpp_dump::index();
cpp_dump::dec(int digits = -1, int chunk = 0);
cpp_dump::bin(int digits = -1, int chunk = 0);
cpp_dump::oct(int digits = -1, int chunk = 0);
cpp_dump::hex(int digits = -1, int chunk = 0);
cpp_dump::map_k(return_value_of_manipulator);
cpp_dump::map_v(return_value_of_manipulator);
cpp_dump::map_kv(return_value_of_manipulator_for_key, return_value_of_manipulator_for_value);

// See 'Customize "[dump]"'.
namespace cpp_dump::log_label {

std::string default_func(const std::string &fullpath, std::size_t line, const std::string &func_name);
log_label_func_t line(bool show_func = false, int min_width = 0);
log_label_func_t basename(bool show_func = false, int min_width = 0);
log_label_func_t filename(bool show_func = false, int min_width = 0);
log_label_func_t fullpath(int substr_start, bool show_func = false, int min_width = 0);
log_label_func_t fixed_length(int min_width, int max_width,
    int substr_start, bool show_func = false);

}

How to print a user-defined type with cpp-dump

There are three ways to enable the library to print a user type.

1. Use CPP_DUMP_DEFINE_EXPORT_OBJECT() macro

This macro requires the user type to be accessible from the top level, but it is the safest and easiest way to enable cpp_dump() to print a user type.
See Full Example Code

// Somewhere accessible from top level (not private or defined in a function)
struct class_A {
  int i;
  std::string str() const { return std::to_string(i); }
};

// At top level
// CPP_DUMP_DEFINE_EXPORT_OBJECT(type_name, members...)
CPP_DUMP_DEFINE_EXPORT_OBJECT(class_A, i, str());

// In a function
class_A my_class_A{10};
cpp_dump(my_class_A);

user-defined-class.png

For enums, use CPP_DUMP_DEFINE_EXPORT_ENUM() macro.
See Full Example Code

// Somewhere accessible from top level (not private or defined in a function)
enum class enum_A { a, b, c };

// At top level
// CPP_DUMP_DEFINE_EXPORT_ENUM(enum_name, members...)
CPP_DUMP_DEFINE_EXPORT_ENUM(enum_A, enum_A::a, enum_A::b, enum_A::c);

// In a function
enum_A my_enum_A = enum_A::c;
cpp_dump(my_enum_A);

user-defined-enum.png

3. Define std::ostream& operator<<(std::ostream&, const T &) operator

The last way is to define the operator std::ostream& operator<<(std::ostream&, const T &).
See Full Example Code

// Somewhere accessible from top level (not private or defined in a function)
struct class_A {
  int i;
  std::string str() const { return std::to_string(i); }
};

// At top level
std::ostream &operator<<(std::ostream &os, const class_A &a) {
  os << "class_A{ i= " << a.i << ", str()= \"" << a.str() << "\" }";
  return os;
}

// In a function
class_A my_class_A{10};
cpp_dump(my_class_A);

user-defined-class3.png

Customize [dump]

Assigning a function to cpp_dump::log_label_func, you can customize [dump].
cpp-dump has some functions that create a function to assign to cpp_dump::log_label_func, so you don’t have to make your own function.

namespace cpp_dump::log_label {

using log_label_func_t =
    std::function<std::string(const std::string &fullpath, std::size_t line, const std::string &func_name)>;

// Default function assigned to cpp_dump::log_label_func.
std::string default_func(const std::string &, std::size_t, const std::string &) {
  return "[dump] ";
}

// Functions that create a function that can be assigned to cpp_dump::log_label_func.
log_label_func_t line(bool show_func = false, int min_width = 0);
log_label_func_t basename(bool show_func = false, int min_width = 0);
log_label_func_t filename(bool show_func = false, int min_width = 0);
log_label_func_t fullpath(int substr_start, bool show_func = false, int min_width = 0);
log_label_func_t fixed_length(int min_width, int max_width,
    int substr_start, bool show_func = false);

}

inline cpp_dump::log_label::log_label_func_t cpp_dump::log_label_func = cpp_dump::log_label::default_func;

Formatting with manipulators

See Full Example Code

Using manipulators, you can set which and how many elements of an array/map/set will be displayed.
See front, middle, back, both_ends manipulators for details.

// Show the last 10 elements for the 1st dimension, the first 5 and the last 5 for the 2nd dimension.
cpp_dump(some_huge_vector | cp::back(10) | cp::both_ends(10));

omitting-a-vector.png

And you can set how integers are displayed with manipulators.
See int_style manipulators for details.

// Show integers in binary, minimum 8 digits, separated by every 2 characters.
cpp_dump(some_huge_vector | cp::bin(8, 2) | cp::front(5) | cp::front(5));

int-style

// Show integers in decimal, minimum 2 digits.
cpp_dump(some_huge_vector | cp::back(10) | cp::both_ends(10) | cp::dec(2));

manipulators.png

Furthermore, you can show the indexes of an array by using a manipulator.
See index manipulator for details.

CPP_DUMP_SET_OPTION(max_iteration_count, 5);

// Show the indexes of the vector.
cpp_dump(some_huge_vector | cp::dec(2) | cp::index());

cont-index.png

front, middle, back, both_ends manipulators

cpp_dump::front(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::middle(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::back(std::size_t iteration_count = cpp_dump::max_iteration_count);
cpp_dump::both_ends(std::size_t iteration_count = cpp_dump::max_iteration_count);

// Example
cpp_dump(cp::front() << cp::back() << variable);
cpp_dump(variable | cp::front() | cp::back());

The further left manipulator will act on the more outside dimensions of the array/map/set.
Caution:
These manipulators other than front() calculate the container’s size. Containers whose size cannot be calculated with std::size() will cost O(N) in computation. In particular, passing an infinite sequence to these manipulators will result in an infinite loop.

index manipulator

cpp_dump::index();

// Example
cpp_dump(... << cp::index() << ... << variable);
cpp_dump(variable | ... | cp::index() | ...);

Like int_style manipulator, this manipulator acts on all sequence containers in the variable. (The order is irrelevant.)
It does not affect maps/sets.

map_* manipulators

cpp_dump::map_k(return_value_of_manipulator);
cpp_dump::map_v(return_value_of_manipulator);
cpp_dump::map_kv(return_value_of_manipulator_for_key, return_value_of_manipulator_for_value);

// Example
cpp_dump(cp::front() << cp::map_kv(cp::hex(), cp::back()) << map);
cpp_dump(map | cp::front() | cp::map_kv(cp::hex(), cp::back()));

These manipulators act on (multi)maps.
In this example, the keys are displayed in hexadecimal, and if the values are iterable, the front part of the values is omitted.

For competitive programming use

#ifdef DEFINED_ONLY_IN_LOCAL
#include "./cpp-dump/dump.hpp"
#define dump(...) cpp_dump(__VA_ARGS__)
namespace cp = cpp_dump;
#else
#define dump(...)
#define CPP_DUMP_SET_OPTION(...)
#define CPP_DUMP_DEFINE_EXPORT_OBJECT(...)
#define CPP_DUMP_DEFINE_EXPORT_ENUM(...)
#define CPP_DUMP_DEFINE_DANGEROUS_EXPORT_OBJECT(...)
#endif

#include <bits/stdc++.h>

#define rep(i, n) for (int i = 0; i < (int)(n); ++i)

using namespace std;

int main() {
  CPP_DUMP_SET_OPTION(max_line_width, 80);
  CPP_DUMP_SET_OPTION(log_label_func, cp::log_label::filename());
  CPP_DUMP_SET_OPTION(enable_asterisk, true);

  int N;
  cin >> N;

  vector<int> X(N);
  rep(i, N) { cin >> X[i]; }
  dump(X);

  // To be continued...
}

Then

g++ ./main.cpp -D DEFINED_ONLY_IN_LOCAL

or

clang++ ./main.cpp -D DEFINED_ONLY_IN_LOCAL

Arithmetic

true, ‘c’, 1, 3.140000

String

“A normal string” A string with '"' or newline(s)

Container

[ value1, value2, … ]

Map

{ key1: value1, key2: value2, … }, { key1 (multiplicity1): [ value1a, value1b, … ], key2 (multiplicity2): [ … ], … }

Set

{ value1, value2, … }, { value1 (multiplicity1), value2 (multiplicity2), … }

Tuple

( value1, value2, … )

FIFO/LIFO

std::queue{ front()= value, back()= value, size()= integer }

Pointer

*value nullptr 0x7fff2246c4d8

(The address will be displayed when the pointer type is void *

Reference

true, ‘c’, 1, 3.140000

(No change)

Exception

std::logic_error{ what()= “Error Message” }


Articles

  • coming soon...