├── .gitignore ├── README.md ├── docs └── screenshot.png ├── index.hxx ├── package.json └── test ├── demo.cxx └── index.cxx /.gitignore: -------------------------------------------------------------------------------- 1 | test/index 2 | test/demo 3 | deps 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SYNOPSIS 2 | A minimalist debugging library inspired by [this][0]. 3 | 4 | # USAGE 5 | 6 | #### INSTALL 7 | This module is designed to work with the [`datcxx`][0] build tool. To add this 8 | module to your project us the following command... 9 | 10 | ```bash 11 | build add heapwolf/debug 12 | ``` 13 | 14 | #### CODE 15 | Construct an instance of `Debug` with a name. The instance will allow you to 16 | toggle the debug output for different parts of your program. 17 | 18 | The DEBUG environment variable is used to enable these based on delimited names. 19 | 20 | ```c++ 21 | #include "./deps/heapwolf/debug/index.hxx" 22 | 23 | Debug debug("demo"); 24 | Debug debug3("demo:beep"); 25 | 26 | void f3 () { 27 | Debug debug2("demo:boop"); 28 | debug2("running function f3"); 29 | debug3("running function f3"); 30 | } 31 | 32 | void f2 () { 33 | debug("running function f2"); 34 | f3(); 35 | } 36 | 37 | int main () { 38 | debug("starting program"); 39 | f2(); 40 | debug("ending program"); 41 | return 0; 42 | } 43 | ``` 44 | 45 | #### OUTPUT 46 | 47 | When actively developing an application it can be useful to see when the time 48 | spent between one debug() call and the next. Suppose for example you invoke 49 | debug(...) before requesting a resource, and after as well, the "+NNNms" will 50 | show you how much time was spent between calls. 51 | 52 | ![example](/docs/screenshot.png) 53 | 54 | 55 | # TEST 56 | 57 | ```bash 58 | build test 59 | ``` 60 | 61 | 62 | # API 63 | 64 | ## CONSTRUCTOR 65 | 66 | ### Debug d(const std::string& name[, std::ostream& stream]) 67 | Construct with a name, optionally specify an output stream. 68 | 69 | [0]:https://github.com/visionmedia/debug 70 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heapwolf/debug/f772f5774cce995eac67da80f21cd992cf5c722c/docs/screenshot.png -------------------------------------------------------------------------------- /index.hxx: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Debug { 12 | using clock_t = std::chrono::high_resolution_clock; 13 | using duration_t = clock_t::duration; 14 | using Str = std::string; 15 | 16 | Str name = ""; 17 | bool enabled = false; 18 | std::ostream& output = std::cerr; 19 | 20 | inline static std::map colors; 21 | inline static int colorIndex = 0; 22 | 23 | const Str RESET = "\033[0m"; 24 | 25 | clock_t::time_point start; 26 | 27 | void init () { 28 | const auto envp = std::getenv("DEBUG"); 29 | 30 | if (envp != nullptr) { 31 | auto env = Str(envp); 32 | 33 | const auto res = replace(env, "*", "(.*)"); 34 | 35 | if (!std::regex_match(this->name, std::regex(res))) { 36 | return; // the env var does not contain the name 37 | } 38 | } else { 39 | return; 40 | } 41 | 42 | this->enabled = true; 43 | 44 | this->start = clock_t::now(); 45 | 46 | if (Debug::colorIndex == 255) { 47 | Debug::colorIndex = 0; 48 | } 49 | 50 | const auto n = std::to_string(Debug::colorIndex++); 51 | Debug::colors[this->name] = "\033[38;5;" + n + "m"; 52 | } 53 | 54 | public: 55 | Debug () { init(); } 56 | 57 | Debug (const Str& str) 58 | : name(str) { init(); }; 59 | 60 | Debug (const Str& str, std::ostream& out) 61 | : name(str), output(out) { init(); }; 62 | 63 | Str ms() const { 64 | using namespace std::literals; 65 | const auto delta = (clock_t::now() - start) / 1ms; 66 | const auto sign = Debug::colors[this->name] + " +"; 67 | 68 | return sign + std::to_string(delta) + "ms" + RESET; 69 | } 70 | 71 | Str replace(Str s, const Str& a, const Str& b) { 72 | Str::size_type pos = 0; 73 | 74 | while ((pos = s.find(a, pos)) != Str::npos) { 75 | s.replace(pos, a.length(), b); 76 | pos += b.length(); 77 | } 78 | 79 | return s; 80 | } 81 | 82 | template 83 | void operator() (Args&&... args) { 84 | if (!this->enabled) { 85 | return; 86 | } 87 | 88 | // 89 | // Print out the name of this instance. 90 | // 91 | const auto color = Debug::colors[this->name]; 92 | this->output << color << this->name << RESET << " "; 93 | 94 | // 95 | // Put all the args and their types into a container 96 | // 97 | std::tuple v { args... }; 98 | 99 | // 100 | // apply() the parameters to a callable function and 101 | // expand them, use a expanded list to print them. 102 | // 103 | auto size = std::tuple_size::value - 1; 104 | 105 | auto print = [this, &size](auto&&... args) { 106 | ( 107 | ( 108 | this->output 109 | << args 110 | << (size-- ? " " : ms()) 111 | ), ... 112 | ); 113 | }; 114 | 115 | std::apply(print, v); 116 | 117 | // 118 | // Print EOL. 119 | // 120 | this->output << std::endl; 121 | 122 | // 123 | // Reset the timer. 124 | // 125 | this->start = clock_t::now(); 126 | } 127 | }; 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "description": "", 4 | "repository": { 5 | "type": "git", 6 | "url": "git@github.com:heapwolf/debug.git" 7 | }, 8 | "dependencies": {}, 9 | "license": "MIT", 10 | "scripts": { 11 | "test": [ 12 | "clang++ -std=c++2a -stdlib=libc++", 13 | "test/index.cxx -o test/index && ./test/index" 14 | ], 15 | "demo": [ 16 | "clang++ -std=c++2a -stdlib=libc++", 17 | "test/demo.cxx -o test/demo && ./test/demo" 18 | ] 19 | }, 20 | "flags": [ 21 | "-std=c++2a", 22 | "-stdlib=stdc++", 23 | "-O3" 24 | ], 25 | "files": [ 26 | "index.hxx" 27 | ], 28 | "devDependencies": { 29 | "git@github.com:heapwolf/cxx-tap": "07821de0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/demo.cxx: -------------------------------------------------------------------------------- 1 | #include "../index.hxx" 2 | 3 | Debug debug("demo"); 4 | Debug debug3("demo:beep"); 5 | 6 | void f3 () { 7 | Debug debug2("demo:boop"); 8 | debug2("running function f3"); 9 | debug3("running function f3"); 10 | } 11 | 12 | 13 | void f2 () { 14 | debug("running function f2"); 15 | f3(); 16 | } 17 | 18 | int main () { 19 | debug("starting program"); 20 | f2(); 21 | debug("ending program"); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/index.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../deps/heapwolf/cxx-tap/index.hxx" 4 | #include "../index.hxx" 5 | 6 | int main() { 7 | TAP::Test t; 8 | 9 | t.test("sanity", [&](auto t) { 10 | t->ok(true, "true is true"); 11 | t->end(); 12 | }); 13 | 14 | t.test("constructor no env var", [&](auto t) { 15 | putenv("DEBUG="); 16 | std::stringstream ss; 17 | Debug debug("foo", ss); 18 | debug(2, 1, 'x'); 19 | 20 | auto actual = ss.str(); 21 | 22 | t->equal(actual, "", "output is empty"); 23 | t->end(); 24 | }); 25 | 26 | t.test("constructor", [&](auto t) { 27 | putenv("DEBUG=foo"); 28 | 29 | std::stringstream ss; 30 | Debug debug("foo", ss); 31 | debug(2, "hello", 'x', 1.1); 32 | 33 | auto actual = ss.str(); 34 | 35 | t->ok(actual.find("foo") != std::string::npos, "output matches"); 36 | t->ok(actual.find("hello x 1.1") != std::string::npos, "output matches"); 37 | 38 | t->end(); 39 | }); 40 | 41 | t.test("time", [&](auto t) { 42 | putenv("DEBUG=foo:*:bazz"); 43 | using namespace std::chrono_literals; 44 | 45 | std::stringstream ss; 46 | Debug x("foo:bar:bazz", ss); 47 | Debug y("foo:boop:bazz", ss); 48 | Debug z("foo:boop:nope", ss); 49 | 50 | for (int i = 0; i < 10; i++) { 51 | std::this_thread::sleep_for(0.01s); 52 | x("test", rand(), "x=" + std::to_string(i)); 53 | y("test", "y=" + std::to_string(i)); 54 | z("test", "z=" + std::to_string(i)); 55 | } 56 | 57 | auto actual = ss.str(); 58 | 59 | for (int i = 0; i < 10; i++) { 60 | t->ok(actual.find("x=" + std::to_string(i)) != std::string::npos, "output matches"); 61 | t->ok(actual.find("y=" + std::to_string(i)) != std::string::npos, "output matches"); 62 | t->ok(actual.find("z=" + std::to_string(i)) == std::string::npos, "output matches"); 63 | } 64 | 65 | t->end(); 66 | }); 67 | } 68 | --------------------------------------------------------------------------------