Include dbg.h to third-party libs
parent
1b201f460f
commit
c8b4cc2ff9
|
@ -239,6 +239,7 @@ target_include_directories(embedded_softfloat PUBLIC
|
|||
)
|
||||
include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include")
|
||||
set(SOFTFLOAT_LIBRARIES embedded_softfloat)
|
||||
include_directories("${CMAKE_SOURCE_DIR}/deps/dbg-macro")
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 David Peter <mail@david-peter.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,172 @@
|
|||
# `dbg(…)`
|
||||
|
||||
[![Build Status](https://travis-ci.org/sharkdp/dbg-macro.svg?branch=master)](https://travis-ci.org/sharkdp/dbg-macro) [![Build status](https://ci.appveyor.com/api/projects/status/vmo9rw4te2wifkul/branch/master?svg=true)](https://ci.appveyor.com/project/sharkdp/dbg-macro) [![Try it online](https://img.shields.io/badge/try-online-f34b7d.svg)](https://repl.it/@sharkdp/dbg-macro-demo) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](dbg.h)
|
||||
|
||||
*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](dbg.h) 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.
|
||||
|
||||
## Examples
|
||||
|
||||
``` c++
|
||||
#include <vector>
|
||||
#include <dbg.h>
|
||||
|
||||
// You can use "dbg(..)" in expressions:
|
||||
int factorial(int n) {
|
||||
if (dbg(n <= 1)) {
|
||||
return dbg(1);
|
||||
} else {
|
||||
return dbg(n * factorial(n - 1));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::string message = "hello";
|
||||
dbg(message); // [example.cpp:15 (main)] message = "hello" (std::string)
|
||||
|
||||
const int a = 2;
|
||||
const int b = dbg(3 * a) + 1; // [example.cpp:18 (main)] 3 * a = 6 (int)
|
||||
|
||||
std::vector<int> numbers{b, 13, 42};
|
||||
dbg(numbers); // [example.cpp:21 (main)] numbers = {7, 13, 42} (size: 3) (std::vector<int>)
|
||||
|
||||
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](https://repl.it/@sharkdp/dbg-macro-demo)):
|
||||
|
||||
![dbg(…) macro output](https://i.imgur.com/NHEYk9A.png)
|
||||
|
||||
## Features
|
||||
|
||||
* 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.
|
||||
|
||||
## Installation
|
||||
|
||||
To make this practical, the `dbg.h` header should to 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/include` or to clone the repository and symlink `dbg.h` to `/usr/include/dbg.h`.
|
||||
``` bash
|
||||
git clone https://github.com/sharkdp/dbg-macro
|
||||
sudo ln -s $(readlink -f dbg-macro/dbg.h) /usr/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.
|
||||
|
||||
### On Arch Linux
|
||||
|
||||
You can install [`dbg-macro` from the AUR](https://aur.archlinux.org/packages/dbg-macro/):
|
||||
``` bash
|
||||
yay -S dbg-macro
|
||||
```
|
||||
|
||||
### With vcpkg
|
||||
|
||||
You can install the [`dbg-macro` port](https://github.com/microsoft/vcpkg/tree/master/ports/dbg-macro) via:
|
||||
``` bash
|
||||
vcpkg install dbg-macro
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
* Set the `DBG_MACRO_DISABLE` flag to disable the `dbg(…)` 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.
|
||||
|
||||
## Advanced features
|
||||
|
||||
### Hexadecimal, octal and binary format
|
||||
|
||||
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(…)`:
|
||||
```c++
|
||||
const uint32_t secret = 12648430;
|
||||
dbg(dbg::hex(secret));
|
||||
```
|
||||
|
||||
### Printing type names
|
||||
|
||||
`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:
|
||||
```c++
|
||||
template <typename T>
|
||||
void my_function_template() {
|
||||
using MyDependentType = typename std::remove_reference<T>::type&&;
|
||||
dbg(dbg::type<MyDependentType>());
|
||||
}
|
||||
```
|
||||
|
||||
### Print the current time
|
||||
|
||||
To print a timestamp, you can use the `dbg::time()` helper:
|
||||
```c++
|
||||
dbg(dbg::time());
|
||||
```
|
||||
|
||||
### Customization
|
||||
|
||||
If you want `dbg(…)` to work for your custom datatype, you can simply overload `operator<<` for
|
||||
`std::ostream&`:
|
||||
```c++
|
||||
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:
|
||||
```c++
|
||||
// Customization point for type information
|
||||
namespace dbg {
|
||||
std::string get_type_name(type_tag<bool>) {
|
||||
return "truth value";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
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:
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_CXX_STANDARD=17
|
||||
make
|
||||
```
|
||||
|
||||
To run the tests, simply call:
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
You can find the unit tests in `tests/basic.cpp`.
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
This project is inspired by Rusts [`dbg!(…)` macro](https://doc.rust-lang.org/std/macro.dbg.html).
|
|
@ -0,0 +1,711 @@
|
|||
/*****************************************************************************
|
||||
|
||||
dbg(...) macro
|
||||
|
||||
License (MIT):
|
||||
|
||||
Copyright (c) 2019 David Peter <mail@david-peter.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef DBG_MACRO_DBG_H
|
||||
#define DBG_MACRO_DBG_H
|
||||
|
||||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#define DBG_MACRO_UNIX
|
||||
#elif defined(_MSC_VER)
|
||||
#define DBG_MACRO_WINDOWS
|
||||
#endif
|
||||
|
||||
#ifndef DBG_MACRO_NO_WARNING
|
||||
#pragma message("WARNING: the 'dbg.h' header is included in your code base")
|
||||
#endif // DBG_MACRO_NO_WARNING
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#ifdef DBG_MACRO_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L || defined(_MSC_VER)
|
||||
#define DBG_MACRO_CXX_STANDARD 17
|
||||
#elif __cplusplus >= 201402L
|
||||
#define DBG_MACRO_CXX_STANDARD 14
|
||||
#else
|
||||
#define DBG_MACRO_CXX_STANDARD 11
|
||||
#endif
|
||||
|
||||
#if DBG_MACRO_CXX_STANDARD >= 17
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#endif
|
||||
|
||||
namespace dbg {
|
||||
|
||||
#ifdef DBG_MACRO_UNIX
|
||||
inline bool isColorizedOutputEnabled() {
|
||||
return isatty(fileno(stderr));
|
||||
}
|
||||
#else
|
||||
inline bool isColorizedOutputEnabled() {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct time {};
|
||||
|
||||
namespace pretty_function {
|
||||
|
||||
// Compiler-agnostic version of __PRETTY_FUNCTION__ and constants to
|
||||
// extract the template argument in `type_name_impl`
|
||||
|
||||
#if defined(__clang__)
|
||||
#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
static constexpr size_t PREFIX_LENGTH =
|
||||
sizeof("const char *dbg::type_name_impl() [T = ") - 1;
|
||||
static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
|
||||
#elif defined(__GNUC__) && !defined(__clang__)
|
||||
#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
static constexpr size_t PREFIX_LENGTH =
|
||||
sizeof("const char* dbg::type_name_impl() [with T = ") - 1;
|
||||
static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
|
||||
#elif defined(_MSC_VER)
|
||||
#define DBG_MACRO_PRETTY_FUNCTION __FUNCSIG__
|
||||
static constexpr size_t PREFIX_LENGTH =
|
||||
sizeof("const char *__cdecl dbg::type_name_impl<") - 1;
|
||||
static constexpr size_t SUFFIX_LENGTH = sizeof(">(void)") - 1;
|
||||
#else
|
||||
#error "This compiler is currently not supported by dbg_macro."
|
||||
#endif
|
||||
|
||||
} // namespace pretty_function
|
||||
|
||||
// Formatting helpers
|
||||
|
||||
template <typename T>
|
||||
struct print_formatted {
|
||||
static_assert(std::is_integral<T>::value,
|
||||
"Only integral types are supported.");
|
||||
|
||||
print_formatted(T value, int numeric_base)
|
||||
: inner(value), base(numeric_base) {}
|
||||
|
||||
operator T() const { return inner; }
|
||||
|
||||
const char* prefix() const {
|
||||
switch (base) {
|
||||
case 8:
|
||||
return "0o";
|
||||
case 16:
|
||||
return "0x";
|
||||
case 2:
|
||||
return "0b";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
T inner;
|
||||
int base;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
print_formatted<T> hex(T value) {
|
||||
return print_formatted<T>{value, 16};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
print_formatted<T> oct(T value) {
|
||||
return print_formatted<T>{value, 8};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
print_formatted<T> bin(T value) {
|
||||
return print_formatted<T>{value, 2};
|
||||
}
|
||||
|
||||
// Implementation of 'type_name<T>()'
|
||||
|
||||
template <typename T>
|
||||
const char* type_name_impl() {
|
||||
return DBG_MACRO_PRETTY_FUNCTION;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct type_tag {};
|
||||
|
||||
template <int&... ExplicitArgumentBarrier, typename T>
|
||||
std::string get_type_name(type_tag<T>) {
|
||||
namespace pf = pretty_function;
|
||||
|
||||
std::string type = type_name_impl<T>();
|
||||
return type.substr(pf::PREFIX_LENGTH,
|
||||
type.size() - pf::PREFIX_LENGTH - pf::SUFFIX_LENGTH);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string type_name() {
|
||||
if (std::is_volatile<T>::value) {
|
||||
if (std::is_pointer<T>::value) {
|
||||
return type_name<typename std::remove_volatile<T>::type>() + " volatile";
|
||||
} else {
|
||||
return "volatile " + type_name<typename std::remove_volatile<T>::type>();
|
||||
}
|
||||
}
|
||||
if (std::is_const<T>::value) {
|
||||
if (std::is_pointer<T>::value) {
|
||||
return type_name<typename std::remove_const<T>::type>() + " const";
|
||||
} else {
|
||||
return "const " + type_name<typename std::remove_const<T>::type>();
|
||||
}
|
||||
}
|
||||
if (std::is_pointer<T>::value) {
|
||||
return type_name<typename std::remove_pointer<T>::type>() + "*";
|
||||
}
|
||||
if (std::is_lvalue_reference<T>::value) {
|
||||
return type_name<typename std::remove_reference<T>::type>() + "&";
|
||||
}
|
||||
if (std::is_rvalue_reference<T>::value) {
|
||||
return type_name<typename std::remove_reference<T>::type>() + "&&";
|
||||
}
|
||||
return get_type_name(type_tag<T>{});
|
||||
}
|
||||
|
||||
inline std::string get_type_name(type_tag<short>) {
|
||||
return "short";
|
||||
}
|
||||
|
||||
inline std::string get_type_name(type_tag<unsigned short>) {
|
||||
return "unsigned short";
|
||||
}
|
||||
|
||||
inline std::string get_type_name(type_tag<long>) {
|
||||
return "long";
|
||||
}
|
||||
|
||||
inline std::string get_type_name(type_tag<unsigned long>) {
|
||||
return "unsigned long";
|
||||
}
|
||||
|
||||
inline std::string get_type_name(type_tag<std::string>) {
|
||||
return "std::string";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string get_type_name(type_tag<std::vector<T, std::allocator<T>>>) {
|
||||
return "std::vector<" + type_name<T>() + ">";
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
std::string get_type_name(type_tag<std::pair<T1, T2>>) {
|
||||
return "std::pair<" + type_name<T1>() + ", " + type_name<T2>() + ">";
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::string type_list_to_string() {
|
||||
std::string result;
|
||||
auto unused = {(result += type_name<T>() + ", ", 0)..., 0};
|
||||
static_cast<void>(unused);
|
||||
|
||||
if (sizeof...(T) > 0) {
|
||||
result.pop_back();
|
||||
result.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::string get_type_name(type_tag<std::tuple<T...>>) {
|
||||
return "std::tuple<" + type_list_to_string<T...>() + ">";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string get_type_name(type_tag<print_formatted<T>>) {
|
||||
return type_name<T>();
|
||||
}
|
||||
|
||||
// Implementation of 'is_detected' to specialize for container-like types
|
||||
|
||||
namespace detail_detector {
|
||||
|
||||
struct nonesuch {
|
||||
nonesuch() = delete;
|
||||
~nonesuch() = delete;
|
||||
nonesuch(nonesuch const&) = delete;
|
||||
void operator=(nonesuch const&) = delete;
|
||||
};
|
||||
|
||||
template <typename...>
|
||||
using void_t = void;
|
||||
|
||||
template <class Default,
|
||||
class AlwaysVoid,
|
||||
template <class...>
|
||||
class Op,
|
||||
class... Args>
|
||||
struct detector {
|
||||
using value_t = std::false_type;
|
||||
using type = Default;
|
||||
};
|
||||
|
||||
template <class Default, template <class...> class Op, class... Args>
|
||||
struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
|
||||
using value_t = std::true_type;
|
||||
using type = Op<Args...>;
|
||||
};
|
||||
|
||||
} // namespace detail_detector
|
||||
|
||||
template <template <class...> class Op, class... Args>
|
||||
using is_detected = typename detail_detector::
|
||||
detector<detail_detector::nonesuch, void, Op, Args...>::value_t;
|
||||
|
||||
namespace detail {
|
||||
|
||||
namespace {
|
||||
using std::begin;
|
||||
using std::end;
|
||||
#if DBG_MACRO_CXX_STANDARD < 17
|
||||
template <typename T>
|
||||
constexpr auto size(const T& c) -> decltype(c.size()) {
|
||||
return c.size();
|
||||
}
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::size_t size(const T (&)[N]) {
|
||||
return N;
|
||||
}
|
||||
#else
|
||||
using std::size;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
using detect_begin_t = decltype(detail::begin(std::declval<T>()));
|
||||
|
||||
template <typename T>
|
||||
using detect_end_t = decltype(detail::end(std::declval<T>()));
|
||||
|
||||
template <typename T>
|
||||
using detect_size_t = decltype(detail::size(std::declval<T>()));
|
||||
|
||||
template <typename T>
|
||||
struct is_container {
|
||||
static constexpr bool value =
|
||||
is_detected<detect_begin_t, T>::value &&
|
||||
is_detected<detect_end_t, T>::value &&
|
||||
is_detected<detect_size_t, T>::value &&
|
||||
!std::is_same<std::string,
|
||||
typename std::remove_cv<
|
||||
typename std::remove_reference<T>::type>::type>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using ostream_operator_t =
|
||||
decltype(std::declval<std::ostream&>() << std::declval<T>());
|
||||
|
||||
template <typename T>
|
||||
struct has_ostream_operator : is_detected<ostream_operator_t, T> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Helper to dbg(…)-print types
|
||||
template <typename T>
|
||||
struct print_type {};
|
||||
|
||||
template <typename T>
|
||||
print_type<T> type() {
|
||||
return print_type<T>{};
|
||||
}
|
||||
|
||||
// Specializations of "pretty_print"
|
||||
|
||||
template <typename T>
|
||||
inline void pretty_print(std::ostream& stream, const T& value, std::true_type) {
|
||||
stream << value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pretty_print(std::ostream&, const T&, std::false_type) {
|
||||
static_assert(detail::has_ostream_operator<const T&>::value,
|
||||
"Type does not support the << ostream operator");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename std::enable_if<!detail::is_container<const T&>::value &&
|
||||
!std::is_enum<T>::value,
|
||||
bool>::type
|
||||
pretty_print(std::ostream& stream, const T& value) {
|
||||
pretty_print(stream, value,
|
||||
typename detail::has_ostream_operator<const T&>::type{});
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool pretty_print(std::ostream& stream, const bool& value) {
|
||||
stream << std::boolalpha << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool pretty_print(std::ostream& stream, const char& value) {
|
||||
const bool printable = value >= 0x20 && value <= 0x7E;
|
||||
|
||||
if (printable) {
|
||||
stream << "'" << value << "'";
|
||||
} else {
|
||||
stream << "'\\x" << std::setw(2) << std::setfill('0') << std::hex
|
||||
<< std::uppercase << (0xFF & value) << "'";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
inline bool pretty_print(std::ostream& stream, P* const& value) {
|
||||
if (value == nullptr) {
|
||||
stream << "nullptr";
|
||||
} else {
|
||||
stream << value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename Deleter>
|
||||
inline bool pretty_print(std::ostream& stream,
|
||||
std::unique_ptr<T, Deleter>& value) {
|
||||
pretty_print(stream, value.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool pretty_print(std::ostream& stream, std::shared_ptr<T>& value) {
|
||||
pretty_print(stream, value.get());
|
||||
stream << " (use_count = " << value.use_count() << ")";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
inline bool pretty_print(std::ostream& stream, const char (&value)[N]) {
|
||||
stream << value;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool pretty_print(std::ostream& stream, const char* const& value) {
|
||||
stream << '"' << value << '"';
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t Idx>
|
||||
struct pretty_print_tuple {
|
||||
template <typename... Ts>
|
||||
static void print(std::ostream& stream, const std::tuple<Ts...>& tuple) {
|
||||
pretty_print_tuple<Idx - 1>::print(stream, tuple);
|
||||
stream << ", ";
|
||||
pretty_print(stream, std::get<Idx>(tuple));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct pretty_print_tuple<0> {
|
||||
template <typename... Ts>
|
||||
static void print(std::ostream& stream, const std::tuple<Ts...>& tuple) {
|
||||
pretty_print(stream, std::get<0>(tuple));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
inline bool pretty_print(std::ostream& stream, const std::tuple<Ts...>& value) {
|
||||
stream << "{";
|
||||
pretty_print_tuple<sizeof...(Ts) - 1>::print(stream, value);
|
||||
stream << "}";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool pretty_print(std::ostream& stream, const std::tuple<>&) {
|
||||
stream << "{}";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool pretty_print(std::ostream& stream, const time&) {
|
||||
using namespace std::chrono;
|
||||
|
||||
const auto now = system_clock::now();
|
||||
const auto us =
|
||||
duration_cast<microseconds>(now.time_since_epoch()).count() % 1000000;
|
||||
const auto hms = system_clock::to_time_t(now);
|
||||
const std::tm* tm = std::localtime(&hms);
|
||||
stream << "current time = " << std::put_time(tm, "%H:%M:%S") << '.'
|
||||
<< std::setw(6) << std::setfill('0') << us;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts decimal integer to binary string
|
||||
template <typename T>
|
||||
std::string decimalToBinary(T n) {
|
||||
const size_t length = 8 * sizeof(T);
|
||||
std::string toRet;
|
||||
toRet.resize(length);
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const auto bit_at_index_i = (n >> i) & 1;
|
||||
toRet[length - 1 - i] = bit_at_index_i + '0';
|
||||
}
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool pretty_print(std::ostream& stream,
|
||||
const print_formatted<T>& value) {
|
||||
if (value.inner < 0) {
|
||||
stream << "-";
|
||||
}
|
||||
stream << value.prefix();
|
||||
|
||||
// Print using setbase
|
||||
if (value.base != 2) {
|
||||
stream << std::setw(sizeof(T)) << std::setfill('0')
|
||||
<< std::setbase(value.base) << std::uppercase;
|
||||
|
||||
if (value.inner >= 0) {
|
||||
// The '+' sign makes sure that a uint_8 is printed as a number
|
||||
stream << +value.inner;
|
||||
} else {
|
||||
using unsigned_type = typename std::make_unsigned<T>::type;
|
||||
stream << +(static_cast<unsigned_type>(-(value.inner + 1)) + 1);
|
||||
}
|
||||
} else {
|
||||
// Print for binary
|
||||
if (value.inner >= 0) {
|
||||
stream << decimalToBinary(value.inner);
|
||||
} else {
|
||||
using unsigned_type = typename std::make_unsigned<T>::type;
|
||||
stream << decimalToBinary<unsigned_type>(
|
||||
static_cast<unsigned_type>(-(value.inner + 1)) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool pretty_print(std::ostream& stream, const print_type<T>&) {
|
||||
stream << type_name<T>();
|
||||
|
||||
stream << " [sizeof: " << sizeof(T) << " byte, ";
|
||||
|
||||
stream << "trivial: ";
|
||||
if (std::is_trivial<T>::value) {
|
||||
stream << "yes";
|
||||
} else {
|
||||
stream << "no";
|
||||
}
|
||||
|
||||
stream << ", standard layout: ";
|
||||
if (std::is_standard_layout<T>::value) {
|
||||
stream << "yes";
|
||||
} else {
|
||||
stream << "no";
|
||||
}
|
||||
stream << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline typename std::enable_if<detail::is_container<const Container&>::value,
|
||||
bool>::type
|
||||
pretty_print(std::ostream& stream, const Container& value) {
|
||||
stream << "{";
|
||||
const size_t size = detail::size(value);
|
||||
const size_t n = std::min(size_t{10}, size);
|
||||
size_t i = 0;
|
||||
using std::begin;
|
||||
using std::end;
|
||||
for (auto it = begin(value); it != end(value) && i < n; ++it, ++i) {
|
||||
pretty_print(stream, *it);
|
||||
if (i != n - 1) {
|
||||
stream << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
if (size > n) {
|
||||
stream << ", ...";
|
||||
stream << " size:" << size;
|
||||
}
|
||||
|
||||
stream << "}";
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Enum>
|
||||
inline typename std::enable_if<std::is_enum<Enum>::value, bool>::type
|
||||
pretty_print(std::ostream& stream, Enum const& value) {
|
||||
using UnderlyingType = typename std::underlying_type<Enum>::type;
|
||||
stream << static_cast<UnderlyingType>(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool pretty_print(std::ostream& stream, const std::string& value) {
|
||||
stream << '"' << value << '"';
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline bool pretty_print(std::ostream& stream, const std::pair<T1, T2>& value) {
|
||||
stream << "{";
|
||||
pretty_print(stream, value.first);
|
||||
stream << ", ";
|
||||
pretty_print(stream, value.second);
|
||||
stream << "}";
|
||||
return true;
|
||||
}
|
||||
|
||||
#if DBG_MACRO_CXX_STANDARD >= 17
|
||||
|
||||
template <typename T>
|
||||
inline bool pretty_print(std::ostream& stream, const std::optional<T>& value) {
|
||||
if (value) {
|
||||
stream << '{';
|
||||
pretty_print(stream, *value);
|
||||
stream << '}';
|
||||
} else {
|
||||
stream << "nullopt";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
inline bool pretty_print(std::ostream& stream,
|
||||
const std::variant<Ts...>& value) {
|
||||
stream << "{";
|
||||
std::visit([&stream](auto&& arg) { pretty_print(stream, arg); }, value);
|
||||
stream << "}";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class DebugOutput {
|
||||
public:
|
||||
DebugOutput(const char* filepath,
|
||||
int line,
|
||||
const char* function_name,
|
||||
const char* expression)
|
||||
: m_use_colorized_output(isColorizedOutputEnabled()),
|
||||
m_filepath(filepath),
|
||||
m_line(line),
|
||||
m_function_name(function_name),
|
||||
m_expression(expression) {
|
||||
const std::size_t path_length = m_filepath.length();
|
||||
if (path_length > MAX_PATH_LENGTH) {
|
||||
m_filepath = ".." + m_filepath.substr(path_length - MAX_PATH_LENGTH,
|
||||
MAX_PATH_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T&& print(const std::string& type, T&& value) const {
|
||||
const T& ref = value;
|
||||
std::stringstream stream_value;
|
||||
const bool print_expr_and_type = pretty_print(stream_value, ref);
|
||||
|
||||
std::stringstream output;
|
||||
output << ansi(ANSI_DEBUG) << "[" << m_filepath << ":" << m_line << " ("
|
||||
<< m_function_name << ")] " << ansi(ANSI_RESET);
|
||||
if (print_expr_and_type) {
|
||||
output << ansi(ANSI_EXPRESSION) << m_expression << ansi(ANSI_RESET)
|
||||
<< " = ";
|
||||
}
|
||||
output << ansi(ANSI_VALUE) << stream_value.str() << ansi(ANSI_RESET);
|
||||
if (print_expr_and_type) {
|
||||
output << " (" << ansi(ANSI_TYPE) << type << ansi(ANSI_RESET) << ")";
|
||||
}
|
||||
output << std::endl;
|
||||
std::cerr << output.str();
|
||||
|
||||
return std::forward<T>(value);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* ansi(const char* code) const {
|
||||
if (m_use_colorized_output) {
|
||||
return code;
|
||||
} else {
|
||||
return ANSI_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
const bool m_use_colorized_output;
|
||||
|
||||
std::string m_filepath;
|
||||
const int m_line;
|
||||
const std::string m_function_name;
|
||||
const std::string m_expression;
|
||||
|
||||
static constexpr std::size_t MAX_PATH_LENGTH = 20;
|
||||
|
||||
static constexpr const char* const ANSI_EMPTY = "";
|
||||
static constexpr const char* const ANSI_DEBUG = "\x1b[02m";
|
||||
static constexpr const char* const ANSI_EXPRESSION = "\x1b[36m";
|
||||
static constexpr const char* const ANSI_VALUE = "\x1b[01m";
|
||||
static constexpr const char* const ANSI_TYPE = "\x1b[32m";
|
||||
static constexpr const char* const ANSI_RESET = "\x1b[0m";
|
||||
};
|
||||
|
||||
// Identity function to suppress "-Wunused-value" warnings in DBG_MACRO_DISABLE
|
||||
// mode
|
||||
template <typename T>
|
||||
T&& identity(T&& t) {
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
} // namespace dbg
|
||||
|
||||
#ifndef DBG_MACRO_DISABLE
|
||||
// We use a variadic macro to support commas inside expressions (e.g.
|
||||
// initializer lists):
|
||||
#define dbg(...) \
|
||||
dbg::DebugOutput(__FILE__, __LINE__, __func__, #__VA_ARGS__) \
|
||||
.print(dbg::type_name<decltype(__VA_ARGS__)>(), (__VA_ARGS__))
|
||||
#else
|
||||
#define dbg(...) dbg::identity(__VA_ARGS__)
|
||||
#endif // DBG_MACRO_DISABLE
|
||||
|
||||
#endif // DBG_MACRO_DBG_H
|
|
@ -19,6 +19,11 @@
|
|||
#include "target.hpp"
|
||||
#include "tokenizer.hpp"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DBG_MACRO_NO_WARNING
|
||||
#include <dbg.h>
|
||||
#endif
|
||||
|
||||
struct AstNode;
|
||||
struct ZigFn;
|
||||
struct Scope;
|
||||
|
|
Loading…
Reference in New Issue