A macro for printf
-style debugging fans.
Debuggers are great. But sometimes you just don't have the time or patience to set up everything correctly and just want a quick way to inspect some values at runtime.
This projects provides a single header file with a dbg(…)
macro that can be used in all circumstances where you would typically write
printf("…", …)
or std::cout << …
. But it comes with a few extras.
#include <dbg.h>
#include <cstdint>
#include <vector>
// You can use "dbg(..)" in expressions:
int32_t factorial(int32_t n) {
if (dbg(n <= 1)) {
return dbg(1);
} else {
return dbg(n * factorial(n - 1));
}
}
int32_t main() {
std::string message = "hello";
dbg(message); // [example.cpp:15 (main)] message = "hello" (std::string)
const int32_t a = 2;
const int32_t b = dbg(3 * a) + 1; // [example.cpp:18 (main)] 3 * a = 6 (int32_t)
std::vector<int32_t> numbers{b, 13, 42};
dbg(numbers); // [example.cpp:21 (main)] numbers = {7, 13, 42} (std::vector<int32_t>)
dbg("this line is executed"); // [example.cpp:23 (main)] this line is executed
factorial(4);
return 0;
}
The code above produces this output (try it yourself):
- Easy to read, colorized output (colors auto-disable when the output is not an interactive terminal)
- Prints file name, line number, function name and the original expression
- Adds type information for the printed-out value
- Specialized pretty-printers for containers, pointers, string literals, enums,
std::optional
, etc. - Can be used inside expressions (passing through the original value)
- The
dbg.h
header issues a compiler warning when included (so you don't forget to remove it). - Compatible and tested with C++11, C++14 and C++17.
To make this practical, the dbg.h
header should be readily available from all kinds of different
places and in all kinds of environments. The quick & dirty way is to actually copy the header file
to /usr/local/include
or to clone the repository and symlink dbg.h
to /usr/local/include/dbg.h
.
git clone https://github.com/sharkdp/dbg-macro
sudo ln -s $(readlink -f dbg-macro/dbg.h) /usr/local/include/dbg.h
If you don't want to make untracked changes to your filesystem, check below if there is a package for your operating system or package manager.
You can install dbg-macro
from the AUR:
yay -S dbg-macro
You can install the dbg-macro
port via:
vcpkg install dbg-macro
CMakeLists.txt
cmake_minimum_required(VERSION 3.11) # FetchContent added in cmake 3.11
project(app) # name of executable
set(CMAKE_CXX_STANDARD 17)
# dbg-macro
include(FetchContent)
FetchContent_Declare(dbg_macro GIT_REPOSITORY https://github.com/sharkdp/dbg-macro)
FetchContent_MakeAvailable(dbg_macro)
add_executable(${PROJECT_NAME} main.cpp) # your source files goes here
target_link_libraries(${PROJECT_NAME} PRIVATE dbg_macro) # make dbg.h available
main.cpp
#include <dbg.h>
int main() {
dbg(42, "hello world", false);
return 0;
}
- Set the
DBG_MACRO_DISABLE
flag to disable thedbg(…)
macro (i.e. to make it a no-op). - Set the
DBG_MACRO_NO_WARNING
flag to disable the "'dbg.h' header is included in your code base" warnings. - Set the
DBG_MACRO_FORCE_COLOR
flag to force colored output and skip tty checks. - Set the
DBG_MACRO_SINGLE_LINE
to print all arguments passed todbg(…)
on a single line.
You can pass multiple arguments to the dbg(…)
macro. The output of
dbg(x, y, z)
is same as dbg(x); dbg(y); dbg(z);
, unless DBG_MACRO_SINGLE_LINE
is defined:
dbg(42, "hello world", false);
Note that you have to wrap "unprotected commas" in parentheses:
dbg("a vector:", (std::vector<int>{2, 3, 4}));
If you want to format integers in hexadecimal, octal or binary representation, you can
simply wrap them in dbg::hex(…)
, dbg::oct(…)
or dbg::bin(…)
:
const uint32_t secret = 12648430;
dbg(dbg::hex(secret));
dbg(…)
already prints the type for each value in parenthesis (see screenshot above). But
sometimes you just want to print a type (maybe because you don't have a value for that type).
In this case, you can use the dbg::type<T>()
helper to pretty-print a given type T
.
For example:
template <typename T>
void my_function_template() {
using MyDependentType = typename std::remove_reference<T>::type&&;
dbg(dbg::type<MyDependentType>());
}
To print a timestamp, you can use the dbg::time()
helper:
dbg(dbg::time());
If you want dbg(…)
to work for your custom datatype, you can simply overload operator<<
for
std::ostream&
:
std::ostream& operator<<(std::ostream& out, const user_defined_type& v) {
out << "…";
return out;
}
If you want to modify the type name that is printed by dbg(…)
, you can add a custom
get_type_name
overload:
// Customization point for type information
namespace dbg {
std::string get_type_name(type_tag<bool>) {
return "truth value";
}
}
If you want to contribute to dbg-macro
, here is how you can build the tests and demos:
Make sure that the submodule(s) are up to date:
git submodule update --init
Then, use the typical cmake
workflow. Usage of -DCMAKE_CXX_STANDARD=17
is optional,
but recommended in order to have the largest set of features enabled:
mkdir build
cd build
cmake .. -DCMAKE_CXX_STANDARD=17
make
To run the tests, simply call:
make test
You can find the unit tests in tests/basic.cpp
.
This project is inspired by Rusts dbg!(…)
macro.