From f9f3c67d7d6ef956e9693768511749f48580cb58 Mon Sep 17 00:00:00 2001 From: Zongyao Jin Date: Wed, 22 Nov 2023 23:31:40 -0800 Subject: [PATCH] adopts google c++ style guide (#9) * done refactoring zj-basics * doing log * utility to do * timer done * all changed * all done * tests examples updated * up * up * up --- .vscode/settings.json | 8 +- README.md | 25 ++-- client-project-example/main.cpp | 33 +++--- cmake/zj-cmake | 2 +- examples/compile-definitions/main.cpp | 2 +- .../CMakeLists.txt | 0 .../inc/foo.hpp | 2 +- .../main.cpp | 4 +- .../src/foo.cpp | 4 +- examples/sigint-sigsegv-handling/main.cpp | 34 +++--- examples/std-any/main.cpp | 10 +- examples/throw-and-try/main.cpp | 28 ++--- examples/timed-msg/main.cpp | 2 +- examples/verify-numerics/main.cpp | 30 ++--- libs/zj-all-headers/inc/zj-log.hpp | 13 -- libs/zj-all-headers/inc/zj-logging.hpp | 13 ++ .../inc/{zj-utility.hpp => zj-tools.hpp} | 6 +- .../{ZjPlaceholder.cpp => zj-placeholder.cpp} | 2 +- libs/zj-basics/inc/ZjChrono.hpp | 66 ----------- ...{ZjBasicMacros.hpp => zj-basic-macros.hpp} | 6 +- libs/zj-basics/inc/zj-chrono.hpp | 63 ++++++++++ .../inc/{ZjColors.hpp => zj-colors.hpp} | 2 +- .../inc/{ZjConcepts.hpp => zj-concepts.hpp} | 2 +- .../inc/{ZjSingleton.hpp => zj-singleton.hpp} | 22 ++-- .../src/{ZjChrono.cpp => zj-chrono.cpp} | 27 ++--- libs/zj-debug-core/inc/ZjLog.hpp | 92 -------------- libs/zj-debug-core/inc/ZjLogAgents.hpp | 100 ---------------- .../inc/{ZjDebug.hpp => zj-debug.hpp} | 87 ++++++-------- .../{ZjExceptions.hpp => zj-exceptions.hpp} | 16 +-- libs/zj-debug-core/inc/zj-formatters.hpp | 15 +++ libs/zj-debug-core/inc/zj-logger.hpp | 92 ++++++++++++++ libs/zj-debug-core/inc/zj-logging-agents.hpp | 87 ++++++++++++++ ...ifyNumerics.hpp => zj-verify-numerics.hpp} | 44 +++---- .../src/{ZjLog.cpp => zj-logger.cpp} | 82 ++++++------- .../inc/ZjLogMacroExtensions.hpp | 43 ------- .../inc/zj-logging-macros-simplified.hpp | 43 +++++++ ...{ZjLogMacros.hpp => zj-logging-macros.hpp} | 36 +++--- .../{ZjPlaceholder.cpp => zj-placeholder.cpp} | 2 +- libs/zj-utility/inc/ZjTimer.hpp | 77 ------------ .../inc/{ZjCsvLog.hpp => zj-csv-logger.hpp} | 74 ++++++------ ...rogramSwitch.hpp => zj-program-switch.hpp} | 18 +-- libs/zj-utility/inc/zj-timer.hpp | 74 ++++++++++++ .../inc/{ZjUtility.hpp => zj-utility.hpp} | 16 +-- libs/zj-utility/src/ZjCsvLog.cpp | 71 ----------- libs/zj-utility/src/ZjProgramSwitch.cpp | 37 ------ libs/zj-utility/src/ZjTimer.cpp | 89 -------------- libs/zj-utility/src/zj-csv-logger.cpp | 71 +++++++++++ libs/zj-utility/src/zj-program-switch.cpp | 37 ++++++ libs/zj-utility/src/zj-timer.cpp | 89 ++++++++++++++ .../{testZjBasics.cpp => test-zj-basics.cpp} | 22 ++-- tests/test-zj-csv-log/main.cpp | 56 ++++----- tests/test-zj-debug-log/main.cpp | 112 +++++++++--------- tests/test-zj-program-switch.cpp | 49 ++++++++ tests/test-zj-singleton.cpp | 94 +++++++++++++++ tests/test-zj-timer.cpp | 62 ++++++++++ tests/test-zj-utility.cpp | 61 ++++++++++ tests/test-zj-verify-numerics/main.cpp | 36 +++--- tests/testZjProgramSwitch.cpp | 49 -------- tests/testZjSingleton.cpp | 94 --------------- tests/testZjTimer.cpp | 62 ---------- tests/testZjUtility.cpp | 61 ---------- 61 files changed, 1264 insertions(+), 1292 deletions(-) rename examples/{modularized => inc-src-structure}/CMakeLists.txt (100%) rename examples/{modularized => inc-src-structure}/inc/foo.hpp (78%) rename examples/{modularized => inc-src-structure}/main.cpp (57%) rename examples/{modularized => inc-src-structure}/src/foo.cpp (64%) delete mode 100644 libs/zj-all-headers/inc/zj-log.hpp create mode 100644 libs/zj-all-headers/inc/zj-logging.hpp rename libs/zj-all-headers/inc/{zj-utility.hpp => zj-tools.hpp} (65%) rename libs/zj-all-headers/src/{ZjPlaceholder.cpp => zj-placeholder.cpp} (81%) delete mode 100644 libs/zj-basics/inc/ZjChrono.hpp rename libs/zj-basics/inc/{ZjBasicMacros.hpp => zj-basic-macros.hpp} (80%) create mode 100644 libs/zj-basics/inc/zj-chrono.hpp rename libs/zj-basics/inc/{ZjColors.hpp => zj-colors.hpp} (99%) rename libs/zj-basics/inc/{ZjConcepts.hpp => zj-concepts.hpp} (98%) rename libs/zj-basics/inc/{ZjSingleton.hpp => zj-singleton.hpp} (77%) rename libs/zj-basics/src/{ZjChrono.cpp => zj-chrono.cpp} (53%) delete mode 100644 libs/zj-debug-core/inc/ZjLog.hpp delete mode 100644 libs/zj-debug-core/inc/ZjLogAgents.hpp rename libs/zj-debug-core/inc/{ZjDebug.hpp => zj-debug.hpp} (78%) rename libs/zj-debug-core/inc/{ZjExceptions.hpp => zj-exceptions.hpp} (86%) create mode 100644 libs/zj-debug-core/inc/zj-formatters.hpp create mode 100644 libs/zj-debug-core/inc/zj-logger.hpp create mode 100644 libs/zj-debug-core/inc/zj-logging-agents.hpp rename libs/zj-debug-core/inc/{ZjVerifyNumerics.hpp => zj-verify-numerics.hpp} (54%) rename libs/zj-debug-core/src/{ZjLog.cpp => zj-logger.cpp} (51%) delete mode 100644 libs/zj-log-interface/inc/ZjLogMacroExtensions.hpp create mode 100644 libs/zj-log-interface/inc/zj-logging-macros-simplified.hpp rename libs/zj-log-interface/inc/{ZjLogMacros.hpp => zj-logging-macros.hpp} (81%) rename libs/zj-log-interface/src/{ZjPlaceholder.cpp => zj-placeholder.cpp} (81%) delete mode 100644 libs/zj-utility/inc/ZjTimer.hpp rename libs/zj-utility/inc/{ZjCsvLog.hpp => zj-csv-logger.hpp} (60%) rename libs/zj-utility/inc/{ZjProgramSwitch.hpp => zj-program-switch.hpp} (78%) create mode 100644 libs/zj-utility/inc/zj-timer.hpp rename libs/zj-utility/inc/{ZjUtility.hpp => zj-utility.hpp} (80%) delete mode 100644 libs/zj-utility/src/ZjCsvLog.cpp delete mode 100644 libs/zj-utility/src/ZjProgramSwitch.cpp delete mode 100644 libs/zj-utility/src/ZjTimer.cpp create mode 100644 libs/zj-utility/src/zj-csv-logger.cpp create mode 100644 libs/zj-utility/src/zj-program-switch.cpp create mode 100644 libs/zj-utility/src/zj-timer.cpp rename tests/{testZjBasics.cpp => test-zj-basics.cpp} (91%) create mode 100644 tests/test-zj-program-switch.cpp create mode 100644 tests/test-zj-singleton.cpp create mode 100644 tests/test-zj-timer.cpp create mode 100644 tests/test-zj-utility.cpp delete mode 100644 tests/testZjProgramSwitch.cpp delete mode 100644 tests/testZjSingleton.cpp delete mode 100644 tests/testZjTimer.cpp delete mode 100644 tests/testZjUtility.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 229e5ee..6fb1d27 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,10 @@ "*.prototxt": "proto", "*.hqt": "cpp", "*.primitives": "xml", + "*.lr": "jinja-html", + "*.lektorproject": "ini", + "*.ini": "ini", + "*.html": "html", "cctype": "cpp", "clocale": "cpp", "cmath": "cpp", @@ -76,9 +80,11 @@ "fstream": "cpp", "forward_list": "cpp", "csignal": "cpp", - "codecvt": "cpp" + "codecvt": "cpp", + "dense": "cpp" }, "cSpell.words": [ + "chrono", "cppcheck", "cpplint", "Eigen", diff --git a/README.md b/README.md index 6d8eac0..4a813a1 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,12 @@ [![C++ Build](https://github.com/zongyaojin/zj-base/actions/workflows/cpp-build.yml/badge.svg)](https://github.com/zongyaojin/zj-base/actions/workflows/cpp-build.yml) [![GitHub license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/zongyaojin/zj-base/blob/main/LICENSE) -This package contains some basic tools such as logging and debugging for C++ projects. - -- `ZjChrono` provides some time-related functions, definitions, and aliases -- `ZjSingleton` provides a singleton base class using the Curiously Recurring Template Pattern - -- `ZjDebug.hpp` provides macros for print, try-catch, and throw mechanisms that can report error with call site and stack trace information -- `ZjVerifyNumerics.hpp` provides a macro to check Eigen or std variable singularity -- `ZjLogMacroExtensions.hpp` provides macros for assertion and regular, condition-based, and periodic message with built-in log support - -- `ZjCsvLog` provides a singleton class that can create as many files as needed and log Eigen vectors with time stamp to a given file in a thread-safe fashion -- `ZjProgramSwitch` provides a singleton class that can be used as a global program switch and run user-specified functions when it turns off -- `ZjTimer` provides a timer class that can be used to guard a periodic loop of a certain frequency -- `ZjUtility.hpp` provides some functions for map access and pointer clean up - -- `[./examples/sigint-sigsegv-handling/]` shows how to handle a Ctrl-C or a Segmentation Fault terminated program -- `[./examples/compile-definitions/]` shows the usage of some compile definitions provided by the [zj-cmake](https://github.com/zongyaojin/zj-cmake/tree/main) submodule +This package provides some basic tools for C++ projects. The package mostly follows the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). But there are a few exceptions: + +- headers use the `#pragma once` include guard; +- regular header and source files use the `.hpp` and `.cpp` extensions; +- some global functions start with an underscore, such as `_ZjMessage()`, indicating that they are not intended for client code to use; +- Macro functions start with an underscore, such as `_ZJ_TRY()` +- Macro variables defined within the package do not have leading or trailing underscores, such as `ZJ_PURPLE`; +- Macro variables defined through CMake follow the two underscores pattern, such as `__FOO_BAR__`; +- filenames follow the `foo-bar.xyz` pattern to make their style consistent with packages such as [zj-cmake](https://github.com/zongyaojin/zj-cmake/tree/main) and [zj-common-scripts](https://github.com/zongyaojin/zj-common-scripts). diff --git a/client-project-example/main.cpp b/client-project-example/main.cpp index a00e9d3..e1a52de 100644 --- a/client-project-example/main.cpp +++ b/client-project-example/main.cpp @@ -1,25 +1,26 @@ -#include "zj-base/zj-log.hpp" -#include "zj-base/zj-utility.hpp" +#include "zj-base/zj-logging.hpp" +#include "zj-base/zj-tools.hpp" #include -#include + +#include "Eigen/Dense" #include "spdlog/spdlog.h" -void foo() +void Foo() { _ZJ_DEBUG("\n\nin foo\n\n"); _ZJ_THROW("intentional fault"); } -void bar() +void Bar() { _ZJ_DEBUG("\n\nin bar\n\n"); - _ZJ_TRY(foo()); + _ZJ_TRY(Foo()); } int main() { - ZjLog::getInstance().init(std::string {__EXAMPLE_PKG_BUILD_PATH__}, std::string {__EXAMPLE_PKG_BUILD_PATH__}); + ZjLogger::GetInstance().Init(std::string {__EXAMPLE_PKG_BUILD_PATH__}, std::string {__EXAMPLE_PKG_BUILD_PATH__}); _ZJ_INFO("hello info"); _ZJ_WARN("hello warn"); @@ -32,10 +33,10 @@ int main() spdlog::critical("Consider selecting PUBLIC, INTERFACE, or PRIVATE"); spdlog::critical("======================================================================="); - _ZJ_INFO("time iso: {}", ZjChrono::getTimeIso()); + _ZJ_INFO("time iso: {}", ZjGetTimeIso()); try { - bar(); + Bar(); } catch (const std::exception& e) { _ZJ_DEBUG("got intentional exception [{}]", e.what()); } @@ -47,15 +48,15 @@ int main() v2.resize(6); v2 << 3.14, 3.14, 3.14, 3.14, 3.14, 3.14; - ZjCsvLog::getInstance().log("csv-example-1", v); - ZjCsvLog::getInstance().log("csv-example-2", v2); + ZjCsvLogger::GetInstance().Log("csv-example-1", v); + ZjCsvLogger::GetInstance().Log("csv-example-2", v2); - ZjCsvLog::getInstance().log("csv-example-1", v); - ZjCsvLog::getInstance().log("csv-example-2", v2); + ZjCsvLogger::GetInstance().Log("csv-example-1", v); + ZjCsvLogger::GetInstance().Log("csv-example-2", v2); - ZjCsvLog::getInstance().log("csv-example-1", v); - ZjCsvLog::getInstance().log("csv-example-2", v2); - ZjCsvLog::getInstance().log("csv-example-2", v2); + ZjCsvLogger::GetInstance().Log("csv-example-1", v); + ZjCsvLogger::GetInstance().Log("csv-example-2", v2); + ZjCsvLogger::GetInstance().Log("csv-example-2", v2); _ZJ_DEBUG(""); _ZJ_INFO("==================================================="); diff --git a/cmake/zj-cmake b/cmake/zj-cmake index 057f595..52e9be8 160000 --- a/cmake/zj-cmake +++ b/cmake/zj-cmake @@ -1 +1 @@ -Subproject commit 057f59575a280b5b1231d761561e108ce7085b3d +Subproject commit 52e9be8bddb350e7932544352bd3a09a0e6c8024 diff --git a/examples/compile-definitions/main.cpp b/examples/compile-definitions/main.cpp index 8c243aa..d4b935b 100644 --- a/examples/compile-definitions/main.cpp +++ b/examples/compile-definitions/main.cpp @@ -1,4 +1,4 @@ -#include "ZjLogMacroExtensions.hpp" +#include "zj-logging-macros-simplified.hpp" int main() { diff --git a/examples/modularized/CMakeLists.txt b/examples/inc-src-structure/CMakeLists.txt similarity index 100% rename from examples/modularized/CMakeLists.txt rename to examples/inc-src-structure/CMakeLists.txt diff --git a/examples/modularized/inc/foo.hpp b/examples/inc-src-structure/inc/foo.hpp similarity index 78% rename from examples/modularized/inc/foo.hpp rename to examples/inc-src-structure/inc/foo.hpp index b5e131c..c8c72fd 100644 --- a/examples/modularized/inc/foo.hpp +++ b/examples/inc-src-structure/inc/foo.hpp @@ -2,4 +2,4 @@ #include -void foo(); +void Foo(); diff --git a/examples/modularized/main.cpp b/examples/inc-src-structure/main.cpp similarity index 57% rename from examples/modularized/main.cpp rename to examples/inc-src-structure/main.cpp index a5e3d70..80bc0c7 100644 --- a/examples/modularized/main.cpp +++ b/examples/inc-src-structure/main.cpp @@ -1,9 +1,9 @@ #include "foo.hpp" -#include "ZjLogMacroExtensions.hpp" +#include "zj-logging-macros-simplified.hpp" int main() { _ZJ_INFO("in main"); - foo(); + Foo(); return 0; } diff --git a/examples/modularized/src/foo.cpp b/examples/inc-src-structure/src/foo.cpp similarity index 64% rename from examples/modularized/src/foo.cpp rename to examples/inc-src-structure/src/foo.cpp index 6b0f55e..1e47e11 100644 --- a/examples/modularized/src/foo.cpp +++ b/examples/inc-src-structure/src/foo.cpp @@ -1,7 +1,7 @@ #include "foo.hpp" -#include "ZjLogMacroExtensions.hpp" +#include "zj-logging-macros-simplified.hpp" -void foo() +void Foo() { Eigen::Vector2d v {3.14, 5.28}; _ZJ_DEBUG("v: {}, {}", v(0), v(1)); diff --git a/examples/sigint-sigsegv-handling/main.cpp b/examples/sigint-sigsegv-handling/main.cpp index 4ef39a3..a76d761 100644 --- a/examples/sigint-sigsegv-handling/main.cpp +++ b/examples/sigint-sigsegv-handling/main.cpp @@ -1,54 +1,54 @@ -#include "zj-log.hpp" -#include "zj-utility.hpp" +#include "zj-logging.hpp" +#include "zj-tools.hpp" #include // Signal handler function for SIGINT (Ctrl-C) -void sigintHandler(int signal) +void SigintHandler(int signal) { _ZJ_WARN("Program terminated by Ctrl-C (SIGINT)."); - ZjLog::getInstance().shutdown(); - ZjProgramSwitch::getInstance().turnOff(); + ZjLogger::GetInstance().Shutdown(); + ZjProgramSwitch::GetInstance().TurnOff(); exit(signal); } // Signal handler function for SIGSEGV (Segmentation fault) -void sigsegvHandler(int signal) +void SigsegvHandler(int signal) { _ZJ_WARN("Segmentation fault (SIGSEGV) occurred."); - ZjLog::getInstance().shutdown(); - ZjProgramSwitch::getInstance().turnOff(); + ZjLogger::GetInstance().Shutdown(); + ZjProgramSwitch::GetInstance().TurnOff(); exit(signal); } -void turnOffRoutine() +void TurnOffRoutine() { _ZJ_DEBUG("turn off routine says bye..."); } int main() { - ZjProgramSwitch::getInstance().turnOn(); - ZjProgramSwitch::getInstance().registerTurnOffRoutine(&turnOffRoutine); + ZjProgramSwitch::GetInstance().TurnOn(); + ZjProgramSwitch::GetInstance().RegisterTurnOffRoutine(&TurnOffRoutine); /// @see for more signals - std::signal(SIGINT, sigintHandler); - std::signal(SIGSEGV, sigsegvHandler); + std::signal(SIGINT, SigintHandler); + std::signal(SIGSEGV, SigsegvHandler); _ZJ_INFO("hello"); std::vector* ptr = nullptr; - auto startTime = ZjChrono::Clock::now(); + auto start_time = ZjChronoClock::now(); while (true) { - auto timeSpent = std::chrono::duration_cast(ZjChrono::Clock::now() - startTime); - _ZJ_INFO_T(1, "Ctrl-C to terminate, or wait 5 sec to trigger segmentation fault, current count: [{}]", timeSpent.count()); - if (timeSpent.count() > 5000) { + auto time_spent = std::chrono::duration_cast(ZjChronoClock::now() - start_time); + _ZJ_INFO_T(1, "Ctrl-C to terminate, or wait 5 sec to trigger segmentation fault, current count: [{}]", time_spent.count()); + if (time_spent.count() > 5000) { _ZJ_WARN("triggering segmentation fault now"); ptr->push_back(1); // cppcheck-suppress nullPointer } diff --git a/examples/std-any/main.cpp b/examples/std-any/main.cpp index 17ae1ae..3114b2d 100644 --- a/examples/std-any/main.cpp +++ b/examples/std-any/main.cpp @@ -1,9 +1,9 @@ -#include "ZjLogMacroExtensions.hpp" +#include "zj-logging-macros-simplified.hpp" #include #include "eigen3/Eigen/Dense" #include -void func(const std::any input) +void Func(const std::any input) { try { auto d = std::any_cast(input); @@ -16,7 +16,7 @@ void func(const std::any input) } -void func2(std::any input) +void Func2(std::any input) { try { double* d = std::any_cast(input); @@ -36,9 +36,9 @@ int main() // std::string a = "2"; // int a = 123; double a = 1.23; - func(a); + Func(a); - func2(&a); + Func2(&a); std::cout << a << std::endl; return 0; diff --git a/examples/throw-and-try/main.cpp b/examples/throw-and-try/main.cpp index 5c32d56..2f51275 100644 --- a/examples/throw-and-try/main.cpp +++ b/examples/throw-and-try/main.cpp @@ -1,9 +1,9 @@ -#include "ZjDebug.hpp" -#include "ZjLogMacroExtensions.hpp" +#include "zj-debug.hpp" +#include "zj-logging-macros-simplified.hpp" -void f1(int i) +void F1(int i) { - spdlog::info("in f1"); + spdlog::info("in F1"); if (i == 2) { throw std::invalid_argument("throwing std exception"); @@ -12,24 +12,24 @@ void f1(int i) _ZJ_THROW_IF(i != 2, "throwing ZjFault"); } -void f2(int i) +void F2(int i) { - spdlog::info("in f2"); - _ZJ_TRY(f1(i)); + spdlog::info("in F2"); + _ZJ_TRY(F1(i)); } -void f3(int i) +void F3(int i) { - spdlog::info("in f3"); - _ZJ_TRY(f2(i)); + spdlog::info("in F3"); + _ZJ_TRY(F2(i)); } int main() { try { - _ZJ_TRY(f3(2)); + _ZJ_TRY(F3(2)); } catch (const std::exception& e) { - spdlog::info("got f3(2) exception: {}", e.what()); + spdlog::info("got F3(2) exception: {}", e.what()); } _ZJ_DEBUG(""); @@ -38,9 +38,9 @@ int main() _ZJ_DEBUG(""); try { - _ZJ_TRY(f3(1)); + _ZJ_TRY(F3(1)); } catch (const std::exception& e) { - spdlog::info("got f3(1) exception: {}", e.what()); + spdlog::info("got F3(1) exception: {}", e.what()); } return 0; diff --git a/examples/timed-msg/main.cpp b/examples/timed-msg/main.cpp index 11541da..343bb4f 100644 --- a/examples/timed-msg/main.cpp +++ b/examples/timed-msg/main.cpp @@ -1,4 +1,4 @@ -#include "ZjLogMacroExtensions.hpp" +#include "zj-logging-macros-simplified.hpp" int main() { diff --git a/examples/verify-numerics/main.cpp b/examples/verify-numerics/main.cpp index d2742c5..a8dabf0 100644 --- a/examples/verify-numerics/main.cpp +++ b/examples/verify-numerics/main.cpp @@ -1,40 +1,40 @@ -#include "ZjVerifyNumerics.hpp" -#include "ZjLogMacroExtensions.hpp" +#include "zj-verify-numerics.hpp" +#include "zj-logging-macros-simplified.hpp" -Eigen::Vector3d invalidEigen() +Eigen::Vector3d InvalidEigen() { Eigen::Vector3d v; v << 1, 2, std::nan("1"); return v; } -double invalidDouble() +double InvalidDouble() { return std::nan("1"); } -void foo(bool flag) +void Foo(bool flag) { if (flag) { - _ZJ_VERIFY(invalidEigen()); + _ZJ_VERIFY(InvalidEigen()); } else { - _ZJ_VERIFY(invalidDouble()); + _ZJ_VERIFY(InvalidDouble()); } } -void bar(bool flag) +void Bar(bool flag) { - _ZJ_TRY(foo(flag)); + _ZJ_TRY(Foo(flag)); } -void hello(bool flag) +void Hello(bool flag) { - _ZJ_TRY(bar(flag)); + _ZJ_TRY(Bar(flag)); } -void world(bool flag) +void World(bool flag) { - _ZJ_TRY(hello(flag)); + _ZJ_TRY(Hello(flag)); } int main() @@ -62,7 +62,7 @@ int main() _ZJ_DEBUG(""); try { - _ZJ_TRY(world(true)); + _ZJ_TRY(World(true)); } catch (const std::exception& e) { _ZJ_INFO("get exception: {}", e.what()); } @@ -74,7 +74,7 @@ int main() _ZJ_DEBUG(""); try { - world(false); + World(false); } catch (const std::exception& e) { _ZJ_INFO("get exception: {}", e.what()); } diff --git a/libs/zj-all-headers/inc/zj-log.hpp b/libs/zj-all-headers/inc/zj-log.hpp deleted file mode 100644 index ec9a3fd..0000000 --- a/libs/zj-all-headers/inc/zj-log.hpp +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @file zj-log.hpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#pragma once - -#include "ZjDebug.hpp" -#include "ZjVerifyNumerics.hpp" -#include "ZjLogMacroExtensions.hpp" -#include "ZjCsvLog.hpp" diff --git a/libs/zj-all-headers/inc/zj-logging.hpp b/libs/zj-all-headers/inc/zj-logging.hpp new file mode 100644 index 0000000..97bc2ce --- /dev/null +++ b/libs/zj-all-headers/inc/zj-logging.hpp @@ -0,0 +1,13 @@ +/** + * @file zj-logging.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#pragma once + +#include "zj-debug.hpp" +#include "zj-verify-numerics.hpp" +#include "zj-logging-macros-simplified.hpp" +#include "zj-csv-logger.hpp" diff --git a/libs/zj-all-headers/inc/zj-utility.hpp b/libs/zj-all-headers/inc/zj-tools.hpp similarity index 65% rename from libs/zj-all-headers/inc/zj-utility.hpp rename to libs/zj-all-headers/inc/zj-tools.hpp index 7cafd47..93c5c43 100644 --- a/libs/zj-all-headers/inc/zj-utility.hpp +++ b/libs/zj-all-headers/inc/zj-tools.hpp @@ -7,6 +7,6 @@ #pragma once -#include "ZjTimer.hpp" -#include "ZjProgramSwitch.hpp" -#include "ZjUtility.hpp" +#include "zj-timer.hpp" +#include "zj-program-switch.hpp" +#include "zj-utility.hpp" diff --git a/libs/zj-all-headers/src/ZjPlaceholder.cpp b/libs/zj-all-headers/src/zj-placeholder.cpp similarity index 81% rename from libs/zj-all-headers/src/ZjPlaceholder.cpp rename to libs/zj-all-headers/src/zj-placeholder.cpp index e85d3ef..51ec68b 100644 --- a/libs/zj-all-headers/src/ZjPlaceholder.cpp +++ b/libs/zj-all-headers/src/zj-placeholder.cpp @@ -1,5 +1,5 @@ /** - * @file ZjPlaceholder.cpp + * @file zj-placeholder.cpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin diff --git a/libs/zj-basics/inc/ZjChrono.hpp b/libs/zj-basics/inc/ZjChrono.hpp deleted file mode 100644 index ca62367..0000000 --- a/libs/zj-basics/inc/ZjChrono.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file ZjChrono.hpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#pragma once - -#include "ZjConcepts.hpp" - -#include -#include - -class ZjChrono final -{ -public: - enum class Unit : std::int8_t - { - Sec = 0, - Ms, - Us, - Ns, - }; - -public: - using Clock = std::chrono::high_resolution_clock; - using TimePoint = Clock::time_point; - using Count = TimePoint::rep; - - using Ns = std::chrono::nanoseconds; - using Us = std::chrono::microseconds; - using Ms = std::chrono::milliseconds; - using Sec = std::chrono::seconds; - -public: - static constexpr double s_nsToSec {1.0e-9}; - static constexpr double s_nsToMs {1.0e-6}; - static constexpr double s_nsToUs {1.0e-3}; - - static constexpr double s_usToSec {1.0e-6}; - static constexpr double s_usToMs {1.0e-3}; - static constexpr double s_usToNs {1.0e+3}; - - static constexpr double s_msToSec {1.0e-3}; - static constexpr double s_msToUs {1.0e+3}; - static constexpr double s_msToNs {1.0e+6}; - - static constexpr double s_secToMs {1.0e+3}; - static constexpr double s_secToUs {1.0e+6}; - static constexpr double s_secToNs {1.0e+9}; - -public: - /// Get epoch time as count in second or sub-second unit - template - inline static auto getTimeEpochCount() - { - return std::chrono::time_point_cast(Clock::now()).time_since_epoch().count(); - } - - /// Get epoch time as double in second - static double getTimeEpoch(const Unit unit); - - /// Get ISO time - static std::string getTimeIso(); -}; diff --git a/libs/zj-basics/inc/ZjBasicMacros.hpp b/libs/zj-basics/inc/zj-basic-macros.hpp similarity index 80% rename from libs/zj-basics/inc/ZjBasicMacros.hpp rename to libs/zj-basics/inc/zj-basic-macros.hpp index d227c68..bae5e81 100644 --- a/libs/zj-basics/inc/ZjBasicMacros.hpp +++ b/libs/zj-basics/inc/zj-basic-macros.hpp @@ -1,5 +1,5 @@ /** - * @file ZjBasicMacros.hpp + * @file zj-basic-macros.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -24,6 +24,6 @@ #define _ZJ_STATIC_BOOLEAN_CHECK(condition) \ do { \ using conditionType = std::remove_cvref_t; \ - constexpr bool boolType {std::is_same_v}; \ - static_assert(boolType, "condition does not evaluate to boolean"); \ + constexpr bool bool_type {std::is_same_v}; \ + static_assert(bool_type, "condition does not evaluate to boolean"); \ } while (0) diff --git a/libs/zj-basics/inc/zj-chrono.hpp b/libs/zj-basics/inc/zj-chrono.hpp new file mode 100644 index 0000000..9cdee37 --- /dev/null +++ b/libs/zj-basics/inc/zj-chrono.hpp @@ -0,0 +1,63 @@ +/** + * @file zj-chrono.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#pragma once + +#include "zj-concepts.hpp" + +#include +#include + +namespace zj { + +constexpr double kNsToSec {1.0e-9}; +constexpr double kNsToMs {1.0e-6}; +constexpr double kNsToUs {1.0e-3}; + +constexpr double kUsToSec {1.0e-6}; +constexpr double kUsToMs {1.0e-3}; +constexpr double kUsToNs {1.0e+3}; + +constexpr double kMsToSec {1.0e-3}; +constexpr double kMsToUs {1.0e+3}; +constexpr double kMsToNs {1.0e+6}; + +constexpr double kSecToMs {1.0e+3}; +constexpr double kSecToUs {1.0e+6}; +constexpr double kSecToNs {1.0e+9}; + +} // namespace zj + +using ZjChronoNs = std::chrono::nanoseconds; +using ZjChronoUs = std::chrono::microseconds; +using ZjChronoMs = std::chrono::milliseconds; +using ZjChronoSec = std::chrono::seconds; + +using ZjChronoClock = std::chrono::high_resolution_clock; +using ZjChronoTimePoint = ZjChronoClock::time_point; +using ZjChronoCount = ZjChronoTimePoint::rep; + +enum class ZjChronoUnit : std::int8_t +{ + kSec = 0, + kMs, + kUs, + kNs, +}; + +/// Get epoch time by count based on duration type +template +inline ZjChronoCount ZjGetTimeEpochCount() +{ + return std::chrono::time_point_cast(ZjChronoClock::now()).time_since_epoch().count(); +} + +/// Get epoch time by double in second +double ZjGetTimeEpoch(const ZjChronoUnit unit); + +/// Get ISO time by string +std::string ZjGetTimeIso(); diff --git a/libs/zj-basics/inc/ZjColors.hpp b/libs/zj-basics/inc/zj-colors.hpp similarity index 99% rename from libs/zj-basics/inc/ZjColors.hpp rename to libs/zj-basics/inc/zj-colors.hpp index d9c3bdb..e04ae8b 100644 --- a/libs/zj-basics/inc/ZjColors.hpp +++ b/libs/zj-basics/inc/zj-colors.hpp @@ -1,5 +1,5 @@ /** - * @file ZjColors.hpp + * @file zj-colors.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin diff --git a/libs/zj-basics/inc/ZjConcepts.hpp b/libs/zj-basics/inc/zj-concepts.hpp similarity index 98% rename from libs/zj-basics/inc/ZjConcepts.hpp rename to libs/zj-basics/inc/zj-concepts.hpp index 895184c..a0e5994 100644 --- a/libs/zj-basics/inc/ZjConcepts.hpp +++ b/libs/zj-basics/inc/zj-concepts.hpp @@ -1,5 +1,5 @@ /** - * @file ZjConcepts.hpp + * @file zj-concepts.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin diff --git a/libs/zj-basics/inc/ZjSingleton.hpp b/libs/zj-basics/inc/zj-singleton.hpp similarity index 77% rename from libs/zj-basics/inc/ZjSingleton.hpp rename to libs/zj-basics/inc/zj-singleton.hpp index 29297d0..1e2a9d6 100644 --- a/libs/zj-basics/inc/ZjSingleton.hpp +++ b/libs/zj-basics/inc/zj-singleton.hpp @@ -1,5 +1,5 @@ /** - * @file ZjSingleton.hpp + * @file zj-singleton.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -18,17 +18,17 @@ * * @warning If the child class needs constructor arguments, then you SHOULD design another wrapper class with the class that requires * constructor arguments as a member variable. For example, you can have a `unique_ptr` member variable, - * and an `Singleton::getInstance().init(...)` function to deal with the initialization of ClassRequiresConstructorArgument. + * and an `Singleton::GetInstance().Init(...)` function to deal with the initialization of ClassRequiresConstructorArgument. * - * @warning If you apply variadic template arguments to the `getInstance(...)` function, it's very confusing since only the input arguments - * used by the first time `Singleton::getInstance(real...)` is called are applied to constructor the instance. The following calls still - * have to provide some dummy arguments such as `Singleton::getInstance(any_dummy...)` to match the function signature, they these dummy + * @warning If you apply variadic template arguments to the `GetInstance(...)` function, it's very confusing since only the input arguments + * used by the first time `Singleton::GetInstance(real...)` is called are applied to constructor the instance. The following calls still + * have to provide some dummy arguments such as `Singleton::GetInstance(any_dummy...)` to match the function signature, they these dummy * arguments could potentially be anything, and will not be used since the instance was already constructed. Yet calling - * `Singleton::getInstance(any_dummy...)` makes it very difficult to the reader to trace what are the real constructor arguments used by the + * `Singleton::GetInstance(any_dummy...)` makes it very difficult to the reader to trace what are the real constructor arguments used by the * instance specially in a program with multiple threads. * - * @warning To summarize, with an `Singleton::getInstance().init(...)` function, the whole program can call it only once, and never use - * it again; which make the initialization easy to trace. In the contrast, calling `Singleton::getInstance(real_or_dummy...)` all over the + * @warning To summarize, with an `Singleton::GetInstance().Init(...)` function, the whole program can call it only once, and never use + * it again; which make the initialization easy to trace. In the contrast, calling `Singleton::GetInstance(real_or_dummy...)` all over the * place isn't a good design. * * @see For example `ZjSingletonWithInit` in `tests/testZjSingleton.cpp`, also comments at https://stackoverflow.com/a/1051076 @@ -37,10 +37,10 @@ template class ZjSingleton { public: - static ChildType& getInstance() + static ChildType& GetInstance() { - static ChildType s_instance; - return s_instance; + static ChildType instance_; + return instance_; } ZjSingleton(const ZjSingleton&) = delete; diff --git a/libs/zj-basics/src/ZjChrono.cpp b/libs/zj-basics/src/zj-chrono.cpp similarity index 53% rename from libs/zj-basics/src/ZjChrono.cpp rename to libs/zj-basics/src/zj-chrono.cpp index e2263f3..5c67c9f 100644 --- a/libs/zj-basics/src/ZjChrono.cpp +++ b/libs/zj-basics/src/zj-chrono.cpp @@ -1,37 +1,36 @@ /** - * @file ZjChrono.cpp + * @file zj-chrono.cpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin */ -#include "ZjChrono.hpp" +#include "zj-chrono.hpp" #include "fmt/chrono.h" -double ZjChrono::getTimeEpoch(const Unit unit) +double ZjGetTimeEpoch(const ZjChronoUnit unit) { - double conversion {0.0}; + double conversion {1.0}; switch (unit) { - case Unit::Ns: - conversion = 1.0; + case ZjChronoUnit::kNs: break; - case Unit::Us: - conversion = s_nsToUs; + case ZjChronoUnit::kUs: + conversion = zj::kNsToUs; break; - case Unit::Ms: - conversion = s_nsToMs; + case ZjChronoUnit::kMs: + conversion = zj::kNsToMs; break; - case Unit::Sec: - conversion = s_nsToSec; + case ZjChronoUnit::kSec: + conversion = zj::kNsToSec; break; default: conversion = 0.0; } - return ZjChrono::getTimeEpochCount() * conversion; + return ZjGetTimeEpochCount() * conversion; } -std::string ZjChrono::getTimeIso() +std::string ZjGetTimeIso() { // https://stackoverflow.com/a/74987040 auto now = std::chrono::system_clock::now(); diff --git a/libs/zj-debug-core/inc/ZjLog.hpp b/libs/zj-debug-core/inc/ZjLog.hpp deleted file mode 100644 index d15a3c6..0000000 --- a/libs/zj-debug-core/inc/ZjLog.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/** - * @file ZjLog.hpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#pragma once - -#include "ZjSingleton.hpp" - -#include -#include -#include - -#include "spdlog/spdlog.h" - -/// Zj log level type enum -enum class ZjLogLevel : std::uint8_t -{ - Trace = 0, - Debug, - Info, - Warn, - Error, - Critical, - Off, -}; - -/// Zj log level short alias -using ZjL = ZjLogLevel; - -/// @brief ZjLog singleton class; @warning This class should NOT use any of the debugging or logging macros, since they rely on ZjLogAgents, -/// which relies on this class to actually log information, using those macros in this class would cause "circular reference" -class ZjLog : public ZjSingleton -{ -public: - using CoreLogLevel = spdlog::level::level_enum; - using CoreLogPtr = std::shared_ptr; - -public: - /** - * @brief Log a message with specified log level; it will start the spdlog thread if it's not already started - * @param level Log level - * @param msg Log message - */ - void log(const ZjLogLevel level, std::string&& msg); - - /** - * @brief Initialize ZjLog and the spdlog thread, which means this should be initialized before any logging - * - * @param csvLogFolderNoSlash Csv log file save folder - * @param regularLogFolderNoSlash Regular log file save folder - * - * @note If csv log folder is empty, csv log files will be saved to this package's build folder - * @note If regular log folder is empty, regular log will only be printed in the console, no file will be saved - */ - void init(const std::string& csvLogFolderNoSlash = "", const std::string& regularLogFolderNoSlash = ""); - - /// Shut down ZjLog and the entire spdlog thread, which means this should be called only when the whole program terminates - void shutdown(); - - /// Log file name - inline const std::string& fileName() const { return m_fileName; } - - /// Regular log folder w/o trailing slash - inline const std::string& regularLogFolder() const { return m_regularLogFolderNoSlash; } - - /// Csv log folder w/o trailing slash - inline const std::string& csvLogFolder() const { return m_csvLogFolderNoSlash; } - -private: - /// Log implementation pointer - CoreLogPtr m_logger; - - /// Regular log file name - std::string m_fileName; - - /// Zj log level to implementation's log level map - const std::unordered_map mk_logLevelMap { - {ZjL::Trace, CoreLogLevel::trace}, - {ZjL::Debug, CoreLogLevel::debug}, - {ZjL::Info, CoreLogLevel::info}, - {ZjL::Warn, CoreLogLevel::warn}, - {ZjL::Error, CoreLogLevel::err}, - {ZjL::Critical, CoreLogLevel::critical}, - {ZjL::Off, CoreLogLevel::off}, - }; - - /// User provided csv log and regular log folder - std::string m_csvLogFolderNoSlash, m_regularLogFolderNoSlash; -}; diff --git a/libs/zj-debug-core/inc/ZjLogAgents.hpp b/libs/zj-debug-core/inc/ZjLogAgents.hpp deleted file mode 100644 index 41afdf4..0000000 --- a/libs/zj-debug-core/inc/ZjLogAgents.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @file ZjLogAgents.hpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - * - * @brief Logging agents to be used in the log macros, the agents deal with the ZjLog class - * @warning Client code should use the macros, not functions here; the log macros use the agents, and the agents interact with the ZjLog - */ - -#pragma once - -#include "ZjLog.hpp" -#include "ZjColors.hpp" - -#include -#include -#include -#include - -#include - -#include "spdlog/spdlog.h" -#include "fmt/format.h" - -namespace zj { -namespace log { -namespace agents { - -/// Assertion message formatter -static constexpr const char* k_assertFmt {"{}:{}:{} @ `{}` | [{}]\n {}\n"}; - -/// Trace message formatter -static constexpr const char* k_traceFmt {"{}:{}:{} @ `{}` | {}"}; - -} // namespace agents -} // namespace log -} // namespace zj - -/** - * @brief Assertion with source location, user messages, and log support - * - * @tparam Args Variadic template arguments for fmt formatted messages - * @param[in] condition Condition literal from macro converted to const char pointer - * @param[in] s Source location - * @param[in] fmt Formatter string - * @param[in] args Variadic template arguments - * - * @warning Client code should use macro _ZJ_ASSERT, not this function, to get condition evaluated and source location taken care of - */ -template -void _ZjAssert(const char* condition, const std::source_location& s, const std::string& fmt = "", Args&&... args) -{ - std::string userMsg {fmt::format(fmt::runtime(fmt), args...)}; - ZjLog::getInstance().log(ZjL::Critical, - fmt::format(zj::log::agents::k_assertFmt, s.file_name(), s.line(), s.column(), s.function_name(), condition, std::move(userMsg))); - ZjLog::getInstance().shutdown(); - std::abort(); -} - -/** - * @brief Message function with source location, user message, and log support - * - * @tparam Args Args Variadic template arguments for fmt formatted messages - * @param[in] level Logging level - * @param[in] s Source location - * @param[in] fmt Formatter string - * @param[in] args Variadic template arguments - * - * @warning Client code should use macros in ZjLogMacroExtensions.hpp, not this function, to get source location taken care of - */ -template -void _ZjMessage(const ZjLogLevel level, const std::source_location& s, const std::string& fmt = "", Args&&... args) -{ - switch (level) { - case ZjLogLevel::Trace: { - std::string userMsg {fmt::format(fmt::runtime(fmt), args...)}; - ZjLog::getInstance().log(ZjL::Trace, - fmt::format(zj::log::agents::k_traceFmt, s.file_name(), s.line(), s.column(), s.function_name(), std::move(userMsg))); - } break; - case ZjLogLevel::Debug: { - ZjLog::getInstance().log(ZjL::Debug, fmt::format(fmt::runtime(fmt), args...)); - } break; - case ZjLogLevel::Info: { - ZjLog::getInstance().log(ZjL::Info, fmt::format(fmt::runtime(fmt), args...)); - } break; - case ZjLogLevel::Warn: { - ZjLog::getInstance().log(ZjL::Warn, fmt::format(fmt::runtime(fmt), args...)); - } break; - case ZjLogLevel::Error: { - ZjLog::getInstance().log(ZjL::Error, fmt::format(fmt::runtime(fmt), args...)); - } break; - case ZjLogLevel::Critical: { - ZjLog::getInstance().log(ZjL::Critical, fmt::format(fmt::runtime(fmt), args...)); - } break; - default: - ZjLog::getInstance().log(ZjL::Critical, fmt::format("unsupported log level, [{}]", static_cast(level))); - std::abort(); - } -} diff --git a/libs/zj-debug-core/inc/ZjDebug.hpp b/libs/zj-debug-core/inc/zj-debug.hpp similarity index 78% rename from libs/zj-debug-core/inc/ZjDebug.hpp rename to libs/zj-debug-core/inc/zj-debug.hpp index 73e571b..61d3a5e 100644 --- a/libs/zj-debug-core/inc/ZjDebug.hpp +++ b/libs/zj-debug-core/inc/zj-debug.hpp @@ -1,5 +1,5 @@ /** - * @file ZjDebug.hpp + * @file zj-debug.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -7,10 +7,11 @@ #pragma once -#include "ZjExceptions.hpp" -#include "ZjBasicMacros.hpp" -#include "ZjColors.hpp" -#include "ZjLogAgents.hpp" +#include "zj-exceptions.hpp" +#include "zj-basic-macros.hpp" +#include "zj-colors.hpp" +#include "zj-formatters.hpp" +#include "zj-logging-agents.hpp" #include #include @@ -22,18 +23,6 @@ #include "spdlog/spdlog.h" #include "fmt/format.h" -namespace zj { -namespace debug { - -/// Debugging information formatter string -static constexpr const char* k_formatter {"{}:{}:{} @ `{}` | {}"}; - -/// Abort message formatter -static constexpr const char* k_printFmt {ZJ_B_PURPLE " ZJ-PRINT" ZJ_PLAIN " | " ZJ_B_WHITE "{}:{}:{} @ `{}`" ZJ_PLAIN " | {}\n"}; - -} // namespace debug -} // namespace zj - /** * @brief A debugging helper that immediately prints without logging; it provides call site trace and takes an optional user message * @@ -51,14 +40,14 @@ static constexpr const char* k_printFmt {ZJ_B_PURPLE " ZJ-PRINT" ZJ_PLAIN " | " template void _ZjPrint(const std::source_location& s, const std::string& fmt = "", Args&&... args) { - using zj::debug::k_printFmt; - - std::string userMsg {fmt::format(fmt::runtime(fmt), args...)}; - if (userMsg.empty()) { - userMsg = "..."; + std::string user_msg {fmt::format(fmt::runtime(fmt), args...)}; + if (user_msg.empty()) { + user_msg = "N/A"; } - std::string fmtMsg {fmt::format(k_printFmt, s.file_name(), s.line(), s.column(), s.function_name(), std::move(userMsg))}; - printf("%s\n", fmtMsg.c_str()); + + constexpr const char* colored_fmt {ZJ_B_PURPLE "ZJ-PRINT" ZJ_PLAIN " | " ZJ_B_WHITE "{}:{}:{} @ `{}`" ZJ_PLAIN " | {}\n"}; + std::string fmt_msg {fmt::format(colored_fmt, s.file_name(), s.line(), s.column(), s.function_name(), std::move(user_msg))}; + printf("%s\n", fmt_msg.c_str()); } /// A macro wrapper for _ZjPrint that provides in-place source location for call site tracing @@ -93,26 +82,24 @@ void _ZjPrint(const std::source_location& s, const std::string& fmt = "", Args&& template void _ZjThrow(const ZjE t, const std::exception& e, const std::source_location& s, const std::string& fmt = "", Args&&... args) { - using zj::debug::k_formatter; - std::string msg {fmt::format(fmt::runtime(fmt), std::forward(args)...)}; - std::string fmtMsg {fmt::format(k_formatter, s.file_name(), s.line(), s.column(), s.function_name(), std::move(msg))}; + std::string fmt_msg {fmt::format(zj::kTraceFmt, s.file_name(), s.line(), s.column(), s.function_name(), std::move(msg))}; switch (t) { - case ZjE::Failure: { - _ZjMessage(ZjLogLevel::Critical, s, fmtMsg); + case ZjE::kFailure: { + _ZjMessage(ZjLogLevel::kCritical, s, fmt_msg); throw ZjFailure(e.what()); } break; - case ZjE::Fault: { - _ZjMessage(ZjLogLevel::Error, s, fmtMsg); + case ZjE::kFault: { + _ZjMessage(ZjLogLevel::kError, s, fmt_msg); throw ZjFault(e.what()); } break; - case ZjE::Singularity: { - _ZjMessage(ZjLogLevel::Error, s, fmtMsg); + case ZjE::kSingularity: { + _ZjMessage(ZjLogLevel::kError, s, fmt_msg); throw ZjSingularity(e.what()); } break; - case ZjE::Bug: { - _ZjMessage(ZjLogLevel::Error, s, fmtMsg); + case ZjE::kBug: { + _ZjMessage(ZjLogLevel::kError, s, fmt_msg); throw ZjBug(e.what()); } break; default: @@ -139,18 +126,18 @@ void _ZjThrow(const ZjE t, const std::exception& e, const std::source_location& try { \ expression; \ } catch (const ZjFailure& e) { \ - _ZjThrow(ZjE::Failure, e, std::source_location::current()); \ + _ZjThrow(ZjE::kFailure, e, std::source_location::current()); \ } catch (const ZjFault& e) { \ - _ZjThrow(ZjE::Fault, e, std::source_location::current()); \ + _ZjThrow(ZjE::kFault, e, std::source_location::current()); \ } catch (const ZjSingularity& e) { \ - _ZjThrow(ZjE::Singularity, e, std::source_location::current()); \ + _ZjThrow(ZjE::kSingularity, e, std::source_location::current()); \ } catch (const ZjBug& e) { \ - _ZjThrow(ZjE::Bug, e, std::source_location::current()); \ + _ZjThrow(ZjE::kBug, e, std::source_location::current()); \ } catch (const std::exception& e) { \ auto s {std::source_location::current()}; \ - auto errMsg {fmt::format("external exception, type [{}], what [{}], from [{}]", _ZJ_DEMANGLE(e), e.what(), #expression)}; \ - auto fmtMsg {fmt::format(zj::debug::k_formatter, s.file_name(), s.line(), s.column(), s.function_name(), std::move(errMsg))}; \ - _ZjThrow(ZjE::Bug, ZjBug(std::move(fmtMsg)), std::source_location::current()); \ + auto err_msg {fmt::format("external exception, type [{}], what [{}], from [{}]", _ZJ_DEMANGLE(e), e.what(), #expression)}; \ + auto fmt_msg {fmt::format(zj::kTraceFmt, s.file_name(), s.line(), s.column(), s.function_name(), std::move(err_msg))}; \ + _ZjThrow(ZjE::kBug, ZjBug(std::move(fmt_msg)), std::source_location::current()); \ } catch (...) { \ _ZjAssert("N/A", std::source_location::current(), "unknown exception, package cannot trace it"); \ } @@ -162,13 +149,13 @@ void _ZjThrow(const ZjE t, const std::exception& e, const std::source_location& #define _ZJ_THROW_EXCEPTION(t, ...) \ do { \ static_assert(std::is_same_v, "first argument of `_ZJ_THROW()` has to be a ZjExceptionType"); \ - static_assert(t != ZjE::Bug, "ZjBug is reserved for `_ZJ_TRY()` to pass upstream exceptions"); \ - static_assert(t != ZjE::Singularity, "ZjSingularity is reserved for `_ZJ_VERIFY()` to check numerics"); \ + static_assert(t != ZjE::kBug, "ZjBug is reserved for `_ZJ_TRY()` to pass upstream exceptions"); \ + static_assert(t != ZjE::kSingularity, "ZjSingularity is reserved for `_ZJ_VERIFY()` to check numerics"); \ switch (t) { \ - case ZjE::Failure: \ + case ZjE::kFailure: \ _ZjThrow(t, ZjFailure(), std::source_location::current(), ##__VA_ARGS__); \ break; \ - case ZjE::Fault: \ + case ZjE::kFault: \ _ZjThrow(t, ZjFault(), std::source_location::current(), ##__VA_ARGS__); \ break; \ default: \ @@ -182,10 +169,10 @@ void _ZjThrow(const ZjE t, const std::exception& e, const std::source_location& // --------------------------------------------------------- /// Throw ZjFault -#define _ZJ_THROW(...) _ZJ_THROW_EXCEPTION(ZjE::Fault, ##__VA_ARGS__) +#define _ZJ_THROW(...) _ZJ_THROW_EXCEPTION(ZjE::kFault, ##__VA_ARGS__) /// Throw ZjFailure -#define _ZJ_THROW_FAILURE(...) _ZJ_THROW_EXCEPTION(ZjE::Failure, ##__VA_ARGS__) +#define _ZJ_THROW_FAILURE(...) _ZJ_THROW_EXCEPTION(ZjE::kFailure, ##__VA_ARGS__) /// @brief Throw ZjFault if condition not satisfied /// @note No need to log the condition since source location will take client to the call site where the condition is present @@ -193,7 +180,7 @@ void _ZjThrow(const ZjE t, const std::exception& e, const std::source_location& do { \ _ZJ_STATIC_BOOLEAN_CHECK(condition); \ if ((condition)) { \ - _ZJ_THROW_EXCEPTION(ZjE::Fault, ##__VA_ARGS__); \ + _ZJ_THROW_EXCEPTION(ZjE::kFault, ##__VA_ARGS__); \ } \ } while (0) @@ -202,6 +189,6 @@ void _ZjThrow(const ZjE t, const std::exception& e, const std::source_location& do { \ _ZJ_STATIC_BOOLEAN_CHECK(condition); \ if ((condition)) { \ - _ZJ_THROW_EXCEPTION(ZjE::Failure, ##__VA_ARGS__); \ + _ZJ_THROW_EXCEPTION(ZjE::kFailure, ##__VA_ARGS__); \ } \ } while (0) diff --git a/libs/zj-debug-core/inc/ZjExceptions.hpp b/libs/zj-debug-core/inc/zj-exceptions.hpp similarity index 86% rename from libs/zj-debug-core/inc/ZjExceptions.hpp rename to libs/zj-debug-core/inc/zj-exceptions.hpp index 8613c71..79ceb8c 100644 --- a/libs/zj-debug-core/inc/ZjExceptions.hpp +++ b/libs/zj-debug-core/inc/zj-exceptions.hpp @@ -1,5 +1,5 @@ /** - * @file ZjExceptions.hpp + * @file zj-exceptions.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -15,10 +15,10 @@ /// ZjException types enum enum class ZjExceptionType : std::uint8_t { - Bug = 0, - Singularity, - Fault, - Failure, + kBug = 0, + kSingularity, + kFault, + kFailure, }; /// ZjExceptionType short alias @@ -32,15 +32,15 @@ class ZjException : public std::exception virtual ~ZjException() noexcept = default; explicit ZjException(const std::string& msg) - : m_msg(msg) + : msg_(msg) { } public: - virtual const char* what() const noexcept { return m_msg.c_str(); } + virtual const char* what() const noexcept { return msg_.c_str(); } protected: - std::string m_msg {"refer to log trace top for more information"}; + std::string msg_ {"refer to log trace top for more information"}; }; /// External level, reserved for catching and re-throwing external, non-zj exceptions diff --git a/libs/zj-debug-core/inc/zj-formatters.hpp b/libs/zj-debug-core/inc/zj-formatters.hpp new file mode 100644 index 0000000..4e03d6d --- /dev/null +++ b/libs/zj-debug-core/inc/zj-formatters.hpp @@ -0,0 +1,15 @@ +/** + * @file zj-formatters.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-11 + * @copyright Copyright 2023 by Zongyao Jin + */ + +#pragma once + +namespace zj { + +constexpr const char* kTraceFmt {"{}:{}:{} @ `{}` | {}"}; +constexpr const char* kAssertFmt {"{}:{}:{} @ `{}` | [{}]\n {}\n"}; + +} // namespace zj diff --git a/libs/zj-debug-core/inc/zj-logger.hpp b/libs/zj-debug-core/inc/zj-logger.hpp new file mode 100644 index 0000000..4c6415d --- /dev/null +++ b/libs/zj-debug-core/inc/zj-logger.hpp @@ -0,0 +1,92 @@ +/** + * @file zj-logger.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#pragma once + +#include "zj-singleton.hpp" + +#include +#include +#include + +#include "spdlog/spdlog.h" + +/// Zj log level type enum +enum class ZjLogLevel : std::uint8_t +{ + kTrace = 0, + kDebug, + kInfo, + kWarn, + kError, + kCritical, + kOff, +}; + +/// Zj log level short alias +using ZjL = ZjLogLevel; + +/// @brief ZjLogger singleton class; @warning This class should NOT use any of the debugging or logging macros, since they rely on +/// ZjLogAgents, which relies on this class to actually log information, using those macros in this class would cause "circular reference" +class ZjLogger : public ZjSingleton +{ +public: + using CoreLogLevel = spdlog::level::level_enum; + using CoreLogPtr = std::shared_ptr; + +public: + /** + * @brief Log a message with specified log level; it will start the spdlog thread if it's not already started + * @param level Log level + * @param msg Log message + */ + void Log(const ZjLogLevel level, std::string&& msg); + + /** + * @brief Initialize ZjLogger and the spdlog thread, which means this should be initialized before any logging + * + * @param csv_log_folder_no_slash Csv log file save folder + * @param regular_log_folder_no_slash Regular log file save folder + * + * @note If csv log folder is empty, csv log files will be saved to this package's build folder + * @note If regular log folder is empty, regular log will only be printed in the console, no file will be saved + */ + void Init(const std::string& csv_log_folder_no_slash = "", const std::string& regular_log_folder_no_slash = ""); + + /// Shut down ZjLogger and the entire spdlog thread, which means this should be called only when the whole program terminates + void Shutdown(); + + /// Log file name + inline const std::string& Filename() const { return filename_; } + + /// Regular log folder w/o trailing slash + inline const std::string& RegularLogFolder() const { return regular_log_folder_no_slash_; } + + /// Csv log folder w/o trailing slash + inline const std::string& CsvLogFolder() const { return csv_log_folder_no_slash_; } + +private: + /// Log implementation pointer + CoreLogPtr logger_; + + /// Regular log file name + std::string filename_; + + /// Zj log level to implementation's log level map + const std::unordered_map log_level_map_ { + {ZjL::kTrace, CoreLogLevel::trace}, + {ZjL::kDebug, CoreLogLevel::debug}, + {ZjL::kInfo, CoreLogLevel::info}, + {ZjL::kWarn, CoreLogLevel::warn}, + {ZjL::kError, CoreLogLevel::err}, + {ZjL::kCritical, CoreLogLevel::critical}, + {ZjL::kOff, CoreLogLevel::off}, + }; + + /// User provided csv log and regular log folder + std::string csv_log_folder_no_slash_, regular_log_folder_no_slash_; +}; diff --git a/libs/zj-debug-core/inc/zj-logging-agents.hpp b/libs/zj-debug-core/inc/zj-logging-agents.hpp new file mode 100644 index 0000000..61a41d8 --- /dev/null +++ b/libs/zj-debug-core/inc/zj-logging-agents.hpp @@ -0,0 +1,87 @@ +/** + * @file zj-logging-agents.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + * + * @brief Logging agents to be used in the log macros, the agents deal with the ZjLogger class + * @warning Client code should use the macros, not functions here; the log macros use the agents, and the agents interact with the ZjLogger + */ + +#pragma once + +#include "zj-logger.hpp" +#include "zj-colors.hpp" +#include "zj-formatters.hpp" + +#include +#include +#include +#include + +#include + +#include "spdlog/spdlog.h" +#include "fmt/format.h" + +/** + * @brief Assertion with source location, user messages, and log support + * + * @tparam Args Variadic template arguments for fmt formatted messages + * @param[in] condition Condition literal from macro converted to const char pointer + * @param[in] s Source location + * @param[in] fmt Formatter string + * @param[in] args Variadic template arguments + * + * @warning Client code should use macro _ZJ_ASSERT, not this function, to get condition evaluated and source location taken care of + */ +template +void _ZjAssert(const char* condition, const std::source_location& s, const std::string& fmt = "", Args&&... args) +{ + std::string user_msg {fmt::format(fmt::runtime(fmt), args...)}; + ZjLogger::GetInstance().Log(ZjL::kCritical, + fmt::format(zj::kAssertFmt, s.file_name(), s.line(), s.column(), s.function_name(), condition, std::move(user_msg))); + ZjLogger::GetInstance().Shutdown(); + std::abort(); +} + +/** + * @brief Message function with source location, user message, and log support + * + * @tparam Args Args Variadic template arguments for fmt formatted messages + * @param[in] level Logging level + * @param[in] s Source location + * @param[in] fmt Formatter string + * @param[in] args Variadic template arguments + * + * @warning Client code should use macros in zj-logging-macros-simplified.hpp, not this function, to get source location taken care of + */ +template +void _ZjMessage(const ZjLogLevel level, const std::source_location& s, const std::string& fmt = "", Args&&... args) +{ + switch (level) { + case ZjLogLevel::kTrace: { + std::string user_msg {fmt::format(fmt::runtime(fmt), args...)}; + ZjLogger::GetInstance().Log(ZjL::kTrace, + fmt::format(zj::kTraceFmt, s.file_name(), s.line(), s.column(), s.function_name(), std::move(user_msg))); + } break; + case ZjLogLevel::kDebug: { + ZjLogger::GetInstance().Log(ZjL::kDebug, fmt::format(fmt::runtime(fmt), args...)); + } break; + case ZjLogLevel::kInfo: { + ZjLogger::GetInstance().Log(ZjL::kInfo, fmt::format(fmt::runtime(fmt), args...)); + } break; + case ZjLogLevel::kWarn: { + ZjLogger::GetInstance().Log(ZjL::kWarn, fmt::format(fmt::runtime(fmt), args...)); + } break; + case ZjLogLevel::kError: { + ZjLogger::GetInstance().Log(ZjL::kError, fmt::format(fmt::runtime(fmt), args...)); + } break; + case ZjLogLevel::kCritical: { + ZjLogger::GetInstance().Log(ZjL::kCritical, fmt::format(fmt::runtime(fmt), args...)); + } break; + default: + ZjLogger::GetInstance().Log(ZjL::kCritical, fmt::format("unsupported log level, [{}]", static_cast(level))); + std::abort(); + } +} diff --git a/libs/zj-debug-core/inc/ZjVerifyNumerics.hpp b/libs/zj-debug-core/inc/zj-verify-numerics.hpp similarity index 54% rename from libs/zj-debug-core/inc/ZjVerifyNumerics.hpp rename to libs/zj-debug-core/inc/zj-verify-numerics.hpp index b0ddefb..d7b4ed7 100644 --- a/libs/zj-debug-core/inc/ZjVerifyNumerics.hpp +++ b/libs/zj-debug-core/inc/zj-verify-numerics.hpp @@ -1,5 +1,5 @@ /** - * @file ZjVerifyNumerics.hpp + * @file zj-verify-numerics.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -7,8 +7,9 @@ #pragma once -#include "ZjConcepts.hpp" -#include "ZjDebug.hpp" +#include "zj-concepts.hpp" +#include "zj-debug.hpp" +#include "zj-formatters.hpp" #include #include @@ -21,13 +22,7 @@ namespace zj { namespace verify { namespace numerics { - -/// Eigen formatter -const Eigen::IOFormat k_eigenFmt {Eigen::StreamPrecision, 0, ", "}; - -/// Exception message formatter -static constexpr const char* k_exceptionFmt {"{}:{}:{} @ `{}` | {}"}; - +const Eigen::IOFormat kEigenFmt {Eigen::StreamPrecision, 0, ", "}; } // namespace numerics } // namespace verify } // namespace zj @@ -40,29 +35,26 @@ static constexpr const char* k_exceptionFmt {"{}:{}:{} @ `{}` | {}"}; * @tparam M Number of rows * @tparam N Number of columns * @param[in] var Variable - * @param[in] varLiteral Variable literal + * @param[in] var_literal Variable literal * @param[in] varSource Variable source location * * @see Keyword `requires` https://en.cppreference.com/w/cpp/language/constraints#Conjunctions */ template requires ZjEigenSizeValid && ZjEigenSizeValid -void _ZjVerifyNumerics(const Eigen::Matrix& var, const std::string& varLiteral, const std::source_location& s) +void _ZjVerifyNumerics(const Eigen::Matrix& var, const std::string& var_literal, const std::source_location& s) { - using zj::verify::numerics::k_eigenFmt; - using zj::verify::numerics::k_exceptionFmt; - if (var.array().isNaN().any() || var.array().isInf().any()) { std::ostringstream oss; if (var.cols() == 1) { - oss << var.transpose().format(k_eigenFmt); + oss << var.transpose().format(zj::verify::numerics::kEigenFmt); } else { - oss << var.format(k_eigenFmt); + oss << var.format(zj::verify::numerics::kEigenFmt); } - std::string errMsg {fmt::format("singular eigen variable [{}]\n{}", varLiteral, oss.str())}; - std::string fmtMsg {fmt::format(k_exceptionFmt, s.file_name(), s.line(), s.column(), s.function_name(), errMsg)}; - _ZjThrow(ZjE::Singularity, ZjSingularity(std::move(fmtMsg)), s, errMsg); + std::string err_msg {fmt::format("singular eigen variable [{}]\n{}", var_literal, oss.str())}; + std::string fmt_msg {fmt::format(zj::kTraceFmt, s.file_name(), s.line(), s.column(), s.function_name(), err_msg)}; + _ZjThrow(ZjE::kSingularity, ZjSingularity(std::move(fmt_msg)), s, err_msg); } } @@ -72,18 +64,16 @@ void _ZjVerifyNumerics(const Eigen::Matrix& var, const std::string& * * @tparam Type Data type * @param[in] var Variable - * @param[in] varLiteral Variable literal + * @param[in] var_literal Variable literal * @param[in] s Variable source location */ template -void _ZjVerifyNumerics(const Type var, const std::string& varLiteral, const std::source_location& s) +void _ZjVerifyNumerics(const Type var, const std::string& var_literal, const std::source_location& s) { - using zj::verify::numerics::k_exceptionFmt; - if (!std::isnormal(var)) { - std::string errMsg {fmt::format("singular std variable [{} = {}]", varLiteral, var)}; - std::string fmtMsg {fmt::format(k_exceptionFmt, s.file_name(), s.line(), s.column(), s.function_name(), errMsg)}; - _ZjThrow(ZjE::Singularity, ZjSingularity(std::move(fmtMsg)), s, errMsg); + std::string err_msg {fmt::format("singular std variable [{} = {}]", var_literal, var)}; + std::string fmt_msg {fmt::format(zj::kTraceFmt, s.file_name(), s.line(), s.column(), s.function_name(), err_msg)}; + _ZjThrow(ZjE::kSingularity, ZjSingularity(std::move(fmt_msg)), s, err_msg); } } diff --git a/libs/zj-debug-core/src/ZjLog.cpp b/libs/zj-debug-core/src/zj-logger.cpp similarity index 51% rename from libs/zj-debug-core/src/ZjLog.cpp rename to libs/zj-debug-core/src/zj-logger.cpp index ab82593..3aa9b9f 100644 --- a/libs/zj-debug-core/src/ZjLog.cpp +++ b/libs/zj-debug-core/src/zj-logger.cpp @@ -1,14 +1,14 @@ /** - * @file ZjLog.cpp + * @file zj-logger.cpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin */ -#include "ZjLog.hpp" -#include "ZjColors.hpp" -#include "ZjChrono.hpp" -#include "ZjBasicMacros.hpp" +#include "zj-logger.hpp" +#include "zj-colors.hpp" +#include "zj-chrono.hpp" +#include "zj-basic-macros.hpp" #include @@ -30,89 +30,89 @@ static constexpr const char* k_logSubFolderName {"zj-logs"}; } // namespace -void ZjLog::log(const ZjLogLevel level, std::string&& msg) +void ZjLogger::Log(const ZjLogLevel level, std::string&& msg) { - // The current version of cpplint doesn't like it, but it could be `if (m_logger) [[unlikely]] {}` with C++ 23 to improve performance - if (m_logger) { - m_logger->log(mk_logLevelMap.at(level), msg); + // The current version of cpplint doesn't like it, but it could be `if (logger_) [[unlikely]] {}` with C++ 23 to improve performance + if (logger_) { + logger_->log(log_level_map_.at(level), msg); return; } - init(); - m_logger->log(mk_logLevelMap.at(level), msg); + Init(); + logger_->log(log_level_map_.at(level), msg); - if (level == ZjL::Critical) { + if (level == ZjL::kCritical) { // Make sure it flushes the logger if the message is critical - m_logger->flush(); + logger_->flush(); } } -void ZjLog::init(const std::string& csvLogFolderNoSlash, const std::string& regularLogFolderNoSlash) +void ZjLogger::Init(const std::string& csv_log_folder_no_slash, const std::string& regular_log_folder_no_slash) { - if (m_logger) { + if (logger_) { return; } - m_csvLogFolderNoSlash = csvLogFolderNoSlash; - m_regularLogFolderNoSlash = regularLogFolderNoSlash; + csv_log_folder_no_slash_ = csv_log_folder_no_slash; + regular_log_folder_no_slash_ = regular_log_folder_no_slash; { auto regularFolderInfo - = m_regularLogFolderNoSlash.empty() - ? fmt::format(ZJ_B_GREEN "ZjLog not saving regular log to file" ZJ_PLAIN) - : fmt::format(ZJ_B_GREEN "ZjLog sets regular log base folder to [{}]" ZJ_PLAIN, m_regularLogFolderNoSlash); - auto csvFolderInfo = m_csvLogFolderNoSlash.empty() - ? fmt::format(ZJ_B_GREEN "ZjLog using default csv log base folder" ZJ_PLAIN) - : fmt::format(ZJ_B_GREEN "ZjLog sets csv log base folder to [{}]" ZJ_PLAIN, m_csvLogFolderNoSlash); + = regular_log_folder_no_slash_.empty() + ? fmt::format(ZJ_B_GREEN "ZjLogger not saving regular log to file" ZJ_PLAIN) + : fmt::format(ZJ_B_GREEN "ZjLogger sets regular log base folder to [{}]" ZJ_PLAIN, regular_log_folder_no_slash); + auto csvFolderInfo = csv_log_folder_no_slash_.empty() + ? fmt::format(ZJ_B_GREEN "ZjLogger using default csv log base folder" ZJ_PLAIN) + : fmt::format(ZJ_B_GREEN "ZjLogger sets csv log base folder to [{}]" ZJ_PLAIN, csv_log_folder_no_slash_); // Logger not initialized, using raw console print printf("[%s | %s]\n", regularFolderInfo.c_str(), csvFolderInfo.c_str()); } // If regular log folder folder is empty, do not create log file, only log to console - if (!m_regularLogFolderNoSlash.empty()) { - std::string logSaveFolder {fmt::format("{}/{}", m_regularLogFolderNoSlash, k_logSubFolderName)}; - m_fileName = fmt::format("{}/{}_{}.txt", logSaveFolder, __ZJ_PKG_NAME__, ZjChrono::getTimeIso()); + if (!regular_log_folder_no_slash_.empty()) { + std::string logSaveFolder {fmt::format("{}/{}", regular_log_folder_no_slash_, k_logSubFolderName)}; + filename_ = fmt::format("{}/{}_{}.txt", logSaveFolder, __ZJ_PKG_NAME__, ZjGetTimeIso()); } else { - m_fileName.clear(); + filename_.clear(); } try { /// @see https://github.com/gabime/spdlog#asynchronous-logger-with-multi-sinks - /// @note Using default thread settings, and one thread should be enough for both ZjLog and a few csv logs + /// @note Using default thread settings, and one thread should be enough for both ZjLogger and a few csv logs spdlog::init_thread_pool(k_defaultLogThreadQSize, k_defaultLogThreadCount); auto stdoutSink {std::make_shared()}; std::vector sinks {stdoutSink}; // If file name is empty, won't create rotating file sink, so no log file will be saved - if (!m_fileName.empty()) { - auto rotatingFileSink {std::make_shared(m_fileName, k_maxLogFileSize, k_maxLogNumFiles)}; + if (!filename_.empty()) { + auto rotatingFileSink {std::make_shared(filename_, k_maxLogFileSize, k_maxLogNumFiles)}; sinks.push_back(rotatingFileSink); } - m_logger = std::make_shared( + logger_ = std::make_shared( __ZJ_PKG_NAME__, sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); } catch (const std::exception& e) { - auto errMsg {fmt::format(ZJ_B_RED "Regular log initialization failed, exception [{} | {}]" ZJ_PLAIN, _ZJ_DEMANGLE(e), e.what())}; - printf("%s\n", errMsg.c_str()); + auto err_msg {fmt::format(ZJ_B_RED "Regular log initialization failed, exception [{} | {}]" ZJ_PLAIN, _ZJ_DEMANGLE(e), e.what())}; + printf("%s\n", err_msg.c_str()); } - m_logger->flush_on(spdlog::level::info); - m_logger->set_level(spdlog::level::trace); + logger_->flush_on(spdlog::level::info); + logger_->set_level(spdlog::level::trace); // https://github.com/gabime/spdlog/wiki/3.-Custom-formatting#pattern-flags // Set time to 3 digits precision after the decimal point - m_logger->set_pattern("%Y-%m-%d %H:%M:%S.%e | %^%l%$ | %v"); + logger_->set_pattern("%Y-%m-%d %H:%M:%S.%e | %^%l%$ | %v"); } -void ZjLog::shutdown() +void ZjLogger::Shutdown() { - if (m_logger) { - m_logger->log(CoreLogLevel::info, "ZjLog shuting down in 3, 2, 1..."); + if (logger_) { + logger_->log(CoreLogLevel::info, "ZjLogger shuting down in 3, 2, 1..."); // Force a flush before shutdown to make sure all messages get logged - m_logger->flush(); + logger_->flush(); } - m_logger.reset(); + logger_.reset(); spdlog::shutdown(); } diff --git a/libs/zj-log-interface/inc/ZjLogMacroExtensions.hpp b/libs/zj-log-interface/inc/ZjLogMacroExtensions.hpp deleted file mode 100644 index f76b859..0000000 --- a/libs/zj-log-interface/inc/ZjLogMacroExtensions.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file ZjLogMacroExtensions.hpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - * - * @brief Simplified log macro interfaces for client code to use - */ - -#pragma once - -#include "ZjLogMacros.hpp" - -/// Assertion interface for client code -#define _ZJ_ASSERT(condition, ...) \ - do { \ - _ZJ_STATIC_BOOLEAN_CHECK(condition); \ - if (!(condition)) { \ - _ZjAssert(#condition, std::source_location::current(), ##__VA_ARGS__); \ - } \ - } while (0) - -/// @{ Message interfaces -#define _ZJ_TRACE(msg, ...) _ZJ_MSG(ZjL::Trace, msg, ##__VA_ARGS__) -#define _ZJ_TRACE_T(intervalSec, msg, ...) _ZJ_MSG_T(ZjL::Trace, intervalSec, msg, ##__VA_ARGS__) -#define _ZJ_TRACE_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::Trace, msg, ##__VA_ARGS__) -#define _ZJ_TRACE_T_IF(condition, intervalSec, msg, ...) _ZJ_MSG_IF(condition, ZjL::Trace, intervalSec, msg, ##__VA_ARGS__) - -#define _ZJ_DEBUG(msg, ...) _ZJ_MSG(ZjL::Debug, msg, ##__VA_ARGS__) -#define _ZJ_DEBUG_T(intervalSec, msg, ...) _ZJ_MSG_T(ZjL::Debug, intervalSec, msg, ##__VA_ARGS__) -#define _ZJ_DEBUG_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::Debug, msg, ##__VA_ARGS__) -#define _ZJ_DEBUG_T_IF(condition, intervalSec, msg, ...) _ZJ_MSG_IF(condition, ZjL::Debug, intervalSec, msg, ##__VA_ARGS__) - -#define _ZJ_INFO(msg, ...) _ZJ_MSG(ZjL::Info, msg, ##__VA_ARGS__) -#define _ZJ_INFO_T(intervalSec, msg, ...) _ZJ_MSG_T(ZjL::Info, intervalSec, msg, ##__VA_ARGS__) -#define _ZJ_INFO_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::Info, msg, ##__VA_ARGS__) -#define _ZJ_INFO_T_IF(condition, intervalSec, msg, ...) _ZJ_MSG_IF(condition, ZjL::Info, intervalSec, msg, ##__VA_ARGS__) - -#define _ZJ_WARN(msg, ...) _ZJ_MSG(ZjL::Warn, msg, ##__VA_ARGS__) -#define _ZJ_WARN_T(intervalSec, msg, ...) _ZJ_MSG_T(ZjL::Warn, intervalSec, msg, ##__VA_ARGS__) -#define _ZJ_WARN_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::Warn, msg, ##__VA_ARGS__) -#define _ZJ_WARN_T_IF(condition, intervalSec, msg, ...) _ZJ_MSG_IF(condition, ZjL::Warn, intervalSec, msg, ##__VA_ARGS__) -/// @} diff --git a/libs/zj-log-interface/inc/zj-logging-macros-simplified.hpp b/libs/zj-log-interface/inc/zj-logging-macros-simplified.hpp new file mode 100644 index 0000000..9c35162 --- /dev/null +++ b/libs/zj-log-interface/inc/zj-logging-macros-simplified.hpp @@ -0,0 +1,43 @@ +/** + * @file zj-logging-macros-simplified.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + * + * @brief Simplified logging macros for client code to use + */ + +#pragma once + +#include "zj-logging-macros.hpp" + +/// Assertion interface for client code +#define _ZJ_ASSERT(condition, ...) \ + do { \ + _ZJ_STATIC_BOOLEAN_CHECK(condition); \ + if (!(condition)) { \ + _ZjAssert(#condition, std::source_location::current(), ##__VA_ARGS__); \ + } \ + } while (0) + +/// @{ Message interfaces +#define _ZJ_TRACE(msg, ...) _ZJ_MSG(ZjL::kTrace, msg, ##__VA_ARGS__) +#define _ZJ_TRACE_T(interval_sec, msg, ...) _ZJ_MSG_T(ZjL::kTrace, interval_sec, msg, ##__VA_ARGS__) +#define _ZJ_TRACE_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::kTrace, msg, ##__VA_ARGS__) +#define _ZJ_TRACE_T_IF(condition, interval_sec, msg, ...) _ZJ_MSG_IF(condition, ZjL::kTrace, interval_sec, msg, ##__VA_ARGS__) + +#define _ZJ_DEBUG(msg, ...) _ZJ_MSG(ZjL::kDebug, msg, ##__VA_ARGS__) +#define _ZJ_DEBUG_T(interval_sec, msg, ...) _ZJ_MSG_T(ZjL::kDebug, interval_sec, msg, ##__VA_ARGS__) +#define _ZJ_DEBUG_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::kDebug, msg, ##__VA_ARGS__) +#define _ZJ_DEBUG_T_IF(condition, interval_sec, msg, ...) _ZJ_MSG_IF(condition, ZjL::kDebug, interval_sec, msg, ##__VA_ARGS__) + +#define _ZJ_INFO(msg, ...) _ZJ_MSG(ZjL::kInfo, msg, ##__VA_ARGS__) +#define _ZJ_INFO_T(interval_sec, msg, ...) _ZJ_MSG_T(ZjL::kInfo, interval_sec, msg, ##__VA_ARGS__) +#define _ZJ_INFO_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::kInfo, msg, ##__VA_ARGS__) +#define _ZJ_INFO_T_IF(condition, interval_sec, msg, ...) _ZJ_MSG_IF(condition, ZjL::kInfo, interval_sec, msg, ##__VA_ARGS__) + +#define _ZJ_WARN(msg, ...) _ZJ_MSG(ZjL::kWarn, msg, ##__VA_ARGS__) +#define _ZJ_WARN_T(interval_sec, msg, ...) _ZJ_MSG_T(ZjL::kWarn, interval_sec, msg, ##__VA_ARGS__) +#define _ZJ_WARN_IF(condition, msg, ...) _ZJ_MSG_IF(condition, ZjL::kWarn, msg, ##__VA_ARGS__) +#define _ZJ_WARN_T_IF(condition, interval_sec, msg, ...) _ZJ_MSG_IF(condition, ZjL::kWarn, interval_sec, msg, ##__VA_ARGS__) +/// @} diff --git a/libs/zj-log-interface/inc/ZjLogMacros.hpp b/libs/zj-log-interface/inc/zj-logging-macros.hpp similarity index 81% rename from libs/zj-log-interface/inc/ZjLogMacros.hpp rename to libs/zj-log-interface/inc/zj-logging-macros.hpp index f4f9c1d..9681d26 100644 --- a/libs/zj-log-interface/inc/ZjLogMacros.hpp +++ b/libs/zj-log-interface/inc/zj-logging-macros.hpp @@ -1,16 +1,16 @@ /** - * @file ZjLogMacros.hpp + * @file zj-logging-macros.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin * - * @warning Client code is recommended to use macros in ZjLogMacroExtensions, not in this file + * @warning Client code is recommended to use macros in `zj-logging-macros-simplified.hpp`, not in this file */ #pragma once -#include "ZjLogAgents.hpp" -#include "ZjBasicMacros.hpp" +#include "zj-logging-agents.hpp" +#include "zj-basic-macros.hpp" #include #include @@ -20,7 +20,7 @@ #define _ZJ_MSG(level, msg, ...) \ do { \ static_assert(std::is_same_v, "level has to be a ZjL"); \ - static_assert(level != ZjL::Error && level != ZjL::Critical, "Error & Critical reserved for _ZjThrow()"); \ + static_assert(level != ZjL::kError && level != ZjL::kCritical, "Error & Critical reserved for _ZjThrow()"); \ _ZjMessage(level, std::source_location::current(), msg, ##__VA_ARGS__); \ } while (0) @@ -33,41 +33,41 @@ } \ } while (0) -/// @brief Periodic message interface that will print/log the message when it's first called, then every intervalSec second(s) +/// @brief Periodic message interface that will print/log the message when it's first called, then every interval_sec second(s) /// @note The macro replacement creates a do-while clause at each call site, and the brackets create a local scope; which means that the /// static variables are defined in these local scopes, so they are unique to each call site, which can help keep track of local "scoped" /// time elapse, which is wonderful -#define _ZJ_MSG_T(level, intervalSec, msg, ...) \ +#define _ZJ_MSG_T(level, interval_sec, msg, ...) \ do { \ - static_assert(std::is_convertible_v, "intervalSec doesn't evaluate to double"); \ - static_assert(intervalSec >= 0.1, "intervalSec should be at least 0.1"); \ + static_assert(std::is_convertible_v, "interval_sec doesn't evaluate to double"); \ + static_assert(interval_sec >= 0.1, "interval_sec should be at least 0.1"); \ \ using Clock = std::chrono::high_resolution_clock; \ using TimePoint = Clock::time_point; \ using DurationNs = std::chrono::nanoseconds; \ \ - static bool firstTime = true; \ - static TimePoint lastTime = Clock::now(); \ + static bool first_time = true; \ + static TimePoint last_time = Clock::now(); \ TimePoint now = Clock::now(); \ - DurationNs elapsed = std::chrono::duration_cast(now - lastTime); \ + DurationNs elapsed = std::chrono::duration_cast(now - last_time); \ \ - if (firstTime) { \ + if (first_time) { \ _ZJ_MSG(level, msg, ##__VA_ARGS__); \ - firstTime = false; \ + first_time = false; \ } \ \ - constexpr std::uint64_t intervalNs = intervalSec * 1e9; \ + constexpr std::uint64_t intervalNs = interval_sec * 1e9; \ if (elapsed > DurationNs {intervalNs}) { \ _ZJ_MSG(level, msg, ##__VA_ARGS__); \ - lastTime = now; \ + last_time = now; \ } \ } while (0) /// Periodic message interface like _ZJ_MSG_T, but this one only logs/prints if the condition is true -#define _ZJ_MSG_T_IF(condition, level, intervalSec, msg, ...) \ +#define _ZJ_MSG_T_IF(condition, level, interval_sec, msg, ...) \ do { \ _ZJ_STATIC_BOOLEAN_CHECK(condition); \ if ((condition)) { \ - _ZJ_MSG_T(level, intervalSec, msg, ##__VA_ARGS__); \ + _ZJ_MSG_T(level, interval_sec, msg, ##__VA_ARGS__); \ } \ } while (0) diff --git a/libs/zj-log-interface/src/ZjPlaceholder.cpp b/libs/zj-log-interface/src/zj-placeholder.cpp similarity index 81% rename from libs/zj-log-interface/src/ZjPlaceholder.cpp rename to libs/zj-log-interface/src/zj-placeholder.cpp index e85d3ef..51ec68b 100644 --- a/libs/zj-log-interface/src/ZjPlaceholder.cpp +++ b/libs/zj-log-interface/src/zj-placeholder.cpp @@ -1,5 +1,5 @@ /** - * @file ZjPlaceholder.cpp + * @file zj-placeholder.cpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin diff --git a/libs/zj-utility/inc/ZjTimer.hpp b/libs/zj-utility/inc/ZjTimer.hpp deleted file mode 100644 index 7e86d44..0000000 --- a/libs/zj-utility/inc/ZjTimer.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @file ZjTimer.hpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#pragma once - -#include "ZjDebug.hpp" -#include "ZjChrono.hpp" - -#include - -#include "fmt/format.h" - -class ZjTimer -{ -public: - explicit ZjTimer(const std::string& name = ""); - ~ZjTimer() = default; - - /** - * @brief Initialize the timer - * - * @param frequency Desired running frequency - * @param overtimeCountLimit The limit of overtime count; if 0 is used, they loop running over time won't trigger anything; otherwise, - * it will trigger designed behaviors in the implementation - */ - void init(const unsigned frequency, const unsigned overtimeCountLimit = 0); - - /// Start time a loop - inline void start() { m_startTime = ZjChrono::Clock::now(); } - - /// Get count since start in Ns, can be used to time a segment - inline ZjChrono::Count sinceStart() const - { - return std::chrono::duration_cast(ZjChrono::Clock::now() - m_startTime).count(); - } - - /** - * @brief Guard the loop, if time spent since start is less than specified period (via frequency), it will speed until then; otherwise - * it will issue warning - * - * @return ZjChrono::Count Number count since start - */ - ZjChrono::Count guard(); - - /// Loop period - double period(const ZjChrono::Unit unit) const; - - /// Overtime count of the last loop - inline ZjChrono::Count overtimeCount() const { return m_overtimeCount; } - - /// Timer name - inline const std::string& name() const { return m_name; } - - /// Total number of counts of all cycles - inline ZjChrono::Count totalCount() const { return m_totalCount; } - - /// Total loop average of all cycles - double totalLoopAvg(const ZjChrono::Unit unit) const; - -private: - std::string m_name {fmt::format("ZjTimer-{}", fmt::ptr(this))}; - - ZjChrono::Count m_overtimeCount {0}; - ZjChrono::Count m_overtimeCountLimit {0}; - - ZjChrono::Ns m_period {0}; - ZjChrono::TimePoint m_startTime {ZjChrono::Clock::now()}; - ZjChrono::Ns m_timeSpent {0}; - ZjChrono::Ns m_timeLeft {0}; - - ZjChrono::Count m_totalCount {0}; - ZjChrono::Count m_totalTimeSpent {0}; -}; diff --git a/libs/zj-utility/inc/ZjCsvLog.hpp b/libs/zj-utility/inc/zj-csv-logger.hpp similarity index 60% rename from libs/zj-utility/inc/ZjCsvLog.hpp rename to libs/zj-utility/inc/zj-csv-logger.hpp index c72d12a..146fa57 100644 --- a/libs/zj-utility/inc/ZjCsvLog.hpp +++ b/libs/zj-utility/inc/zj-csv-logger.hpp @@ -1,5 +1,5 @@ /** - * @file ZjCsvLog.hpp + * @file zj-csv-logger.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -7,10 +7,10 @@ #pragma once -#include "ZjSingleton.hpp" -#include "ZjConcepts.hpp" -#include "ZjLogMacros.hpp" -#include "ZjDebug.hpp" +#include "zj-singleton.hpp" +#include "zj-concepts.hpp" +#include "zj-logging-macros.hpp" +#include "zj-debug.hpp" #include #include @@ -21,9 +21,9 @@ #include "spdlog/spdlog.h" /// @brief A csv log class that can create and log to as many files as needed in a thread safe fashion -class ZjCsvLog : public ZjSingleton +class ZjCsvLogger : public ZjSingleton { - using CoreLogPtr = ZjLog::CoreLogPtr; + using CoreLogPtr = ZjLogger::CoreLogPtr; using LogName = std::string; using DataSize = Eigen::Index; @@ -36,60 +36,60 @@ class ZjCsvLog : public ZjSingleton private: /// @brief A inner class that represents a logger that only logs data to a single file for the outer class to create and employ - class ZjCsvLogWorker + class ZjCsvLoggingUnit { /// @see https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html - const Eigen::IOFormat mk_eigenFmt {9, 0, ", "}; + const Eigen::IOFormat eigen_fmt_ {9, 0, ", "}; public: - ZjCsvLogWorker() = default; - ~ZjCsvLogWorker() = default; + ZjCsvLoggingUnit() = default; + ~ZjCsvLoggingUnit() = default; /// @note https://github.com/cpp-best-practices/cppbestpractices/blob/master/08-Considering_Performance.md#enable-move-operations /// @note https://en.cppreference.com/w/cpp/language/move_constructor, enabling move constructor for default shallow coping - /// @see Employed by ZjCsvLog::log - ZjCsvLogWorker(ZjCsvLogWorker&&) = default; + /// @see Employed by ZjCsvLogger::log + ZjCsvLoggingUnit(ZjCsvLoggingUnit&&) = default; /** * @brief Log data, it will check to make sure each time the data to be logged is consistent with previous data's dimension * * @tparam T Data type * @tparam N Data dimension - * @param logName Log name + * @param log_name Log name * @param data Data * * @note If the given log name doesn't exist, it will initialized the log and file and log the data; otherwise, data will be logged * to the existing log and file * - * @note This is managed by the outer class, the outer class will always use the same log name to access a ZjCsvLogWorker instance; + * @note This is managed by the outer class, the outer class will always use the same log name to access a ZjCsvLoggingUnit instance; * technically, if this class takes a new log name, the existing one will be overwritten, but the outer class won't do that */ template - void log(const std::string& logName, const EigenVec& data) + void Log(const std::string& log_name, const EigenVec& data) { // Create a log if not already exists - if (!m_logger) [[unlikely]] { - init(logName, data.size()); + if (!logger_) [[unlikely]] { + Init(log_name, data.size()); } - _ZJ_THROW_IF(data.size() != m_dataSize, "inconsistent data size [{} | {}]", data.size(), m_dataSize); + _ZJ_THROW_IF(data.size() != data_size_, "inconsistent data size [{} | {}]", data.size(), data_size_); std::ostringstream oss; - oss << data.transpose().format(mk_eigenFmt); - m_logger->info("{}", oss.str()); + oss << data.transpose().format(eigen_fmt_); + logger_->info("{}", oss.str()); } /// Log's file name - const std::string& fileName() const { return m_fileName; } + const std::string& Filename() const { return filename_; } private: /// @brief Initialize the logger with a name, and the size of data to-be logged to keep track of data consistence - void init(const std::string& logName, const DataSize dataSize); + void Init(const std::string& log_name, const DataSize data_size); - CoreLogPtr m_logger; - DataSize m_dataSize; + CoreLogPtr logger_; + DataSize data_size_; - std::string m_logName; - std::string m_fileName; + std::string log_name_; + std::string filename_; }; public: @@ -99,28 +99,28 @@ class ZjCsvLog : public ZjSingleton * * @tparam T Data type * @tparam N Data dimension - * @param logName Log name + * @param log_name Log name * @param data Data */ template - void log(const std::string& logName, const EigenVec& data) + void Log(const std::string& log_name, const EigenVec& data) { - /// @note `m_logWorkerMap` is a map, it will automatically handle/ignore duplication, no need to manually search here + /// @note `logging_units_` is a map, it will automatically handle/ignore duplication, no need to manually search here /// @note https://stackoverflow.com/a/27553958, with default move constructor, it will be moved - m_logWorkerMap.emplace(logName, ZjCsvLogWorker {}); - m_logWorkerMap.at(logName).log(logName, data); + logging_units_.emplace(log_name, ZjCsvLoggingUnit {}); + logging_units_.at(log_name).Log(log_name, data); } /// Drop a given log if it exists - void drop(const std::string& logName); + void Drop(const std::string& log_name); /// Number of active logs at the moment - inline auto numLogs() const { return m_logWorkerMap.size(); } + inline auto NumLogs() const { return logging_units_.size(); } /// Get a log's file name - std::string fileName(const std::string& logName); + std::string Filename(const std::string& log_name); private: - /// A map that keeps track of ZjCsvLogWorker by their unique log name - std::unordered_map m_logWorkerMap; + /// A map that keeps track of ZjCsvLoggingUnit by their unique log name + std::unordered_map logging_units_; }; diff --git a/libs/zj-utility/inc/ZjProgramSwitch.hpp b/libs/zj-utility/inc/zj-program-switch.hpp similarity index 78% rename from libs/zj-utility/inc/ZjProgramSwitch.hpp rename to libs/zj-utility/inc/zj-program-switch.hpp index 5706001..44254ed 100644 --- a/libs/zj-utility/inc/ZjProgramSwitch.hpp +++ b/libs/zj-utility/inc/zj-program-switch.hpp @@ -1,5 +1,5 @@ /** - * @file ZjProgramSwitch.hpp + * @file zj-program-switch.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -7,8 +7,8 @@ #pragma once -#include "ZjSingleton.hpp" -#include "ZjDebug.hpp" +#include "zj-singleton.hpp" +#include "zj-debug.hpp" #include @@ -21,9 +21,9 @@ class ZjProgramSwitch : public ZjSingleton { public: - inline bool on() { return m_on; } + inline bool On() { return on_; } - inline void turnOn() { m_on = true; } + inline void TurnOn() { on_ = true; } /** * @brief Turn off the program switch, and before that, execute all registered functions @@ -32,13 +32,13 @@ class ZjProgramSwitch : public ZjSingleton * operators implemented, you cannot change the container to std::set<>, it won't compile; comparing two pointers directly also doesn't * seem work */ - void turnOff(); + void TurnOff(); /// Register functions to be executed before turning off - void registerTurnOffRoutine(const std::function& routine); + void RegisterTurnOffRoutine(const std::function& routine); private: - bool m_on {true}; + bool on_ {true}; - std::vector> m_turnOffRoutine; + std::vector> turn_off_routines_; }; diff --git a/libs/zj-utility/inc/zj-timer.hpp b/libs/zj-utility/inc/zj-timer.hpp new file mode 100644 index 0000000..865d4ae --- /dev/null +++ b/libs/zj-utility/inc/zj-timer.hpp @@ -0,0 +1,74 @@ +/** + * @file zj-timer.hpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#pragma once + +#include "zj-debug.hpp" +#include "zj-chrono.hpp" + +#include + +#include "fmt/format.h" + +class ZjTimer +{ +public: + explicit ZjTimer(const std::string& name = ""); + ~ZjTimer() = default; + + /** + * @brief Initialize the timer + * + * @param frequency Desired running frequency + * @param overtime_count_limit The limit of overtime count; if 0 is used, they loop running over time won't trigger anything; + * otherwise, it will trigger designed behaviors in the implementation + */ + void Init(const unsigned frequency, const unsigned overtime_count_limit = 0); + + /// Start time a loop + inline void Start() { start_time_ = ZjChronoClock::now(); } + + /// Get count since start in Ns, can be used to time a segment + inline ZjChronoCount SinceStart() const { return std::chrono::duration_cast(ZjChronoClock::now() - start_time_).count(); } + + /** + * @brief Guard the loop, if time spent since start is less than specified period (via frequency), it will speed until then; otherwise + * it will issue warning + * + * @return ZjChronoCount Number count since start + */ + ZjChronoCount Guard(); + + /// Loop period + double Period(const ZjChronoUnit unit) const; + + /// Overtime count of the last loop + inline ZjChronoCount OvertimeCount() const { return overtime_count_; } + + /// Timer name + inline const std::string& Name() const { return name_; } + + /// Total number of counts of all cycles + inline ZjChronoCount TotalCount() const { return total_count_; } + + /// Total loop average of all cycles + double TotalLoopAverage(const ZjChronoUnit unit) const; + +private: + std::string name_ {fmt::format("ZjTimer-{}", fmt::ptr(this))}; + + ZjChronoCount overtime_count_ {0}; + ZjChronoCount overtime_count_limit_ {0}; + + ZjChronoNs period_ {0}; + ZjChronoTimePoint start_time_ {ZjChronoClock::now()}; + ZjChronoNs time_spent_ {0}; + ZjChronoNs time_left_ {0}; + + ZjChronoCount total_count_ {0}; + ZjChronoCount total_time_spent {0}; +}; diff --git a/libs/zj-utility/inc/ZjUtility.hpp b/libs/zj-utility/inc/zj-utility.hpp similarity index 80% rename from libs/zj-utility/inc/ZjUtility.hpp rename to libs/zj-utility/inc/zj-utility.hpp index f654bc3..c8a588c 100644 --- a/libs/zj-utility/inc/ZjUtility.hpp +++ b/libs/zj-utility/inc/zj-utility.hpp @@ -1,5 +1,5 @@ /** - * @file ZjUtility.hpp + * @file zj-utility.hpp * @author Zongyao Jin (zongyaojin@outlook.com) * @date 2023-08 * @copyright Copyright (c) 2023 by Zongyao Jin @@ -7,9 +7,9 @@ #pragma once -#include "ZjDebug.hpp" -#include "ZjLogMacroExtensions.hpp" -#include "ZjConcepts.hpp" +#include "zj-debug.hpp" +#include "zj-logging-macros-simplified.hpp" +#include "zj-concepts.hpp" #include #include @@ -35,7 +35,7 @@ * ``` */ template -typename MapType::key_type _GetKeyByValue(const MapType& map, const ValueType& value) +typename MapType::key_type ZjGetKeyByValue(const MapType& map, const ValueType& value) { for (const auto& pair : map) { if (pair.second == value) { // cppcheck-suppress useStlAlgorithm @@ -48,9 +48,9 @@ typename MapType::key_type _GetKeyByValue(const MapType& map, const ValueType& v } /// @brief Get enum (key) by string (value) from an unordered_map of enum and string -/// @see _GetKeyByValue for notes +/// @see ZjGetKeyByValue for notes template -T _GetEnumByString(const std::unordered_map& map, const std::string& name) +T ZjGetEnumByString(const std::unordered_map& map, const std::string& name) { for (const auto& pair : map) { if (pair.second == name) { // cppcheck-suppress useStlAlgorithm @@ -64,7 +64,7 @@ T _GetEnumByString(const std::unordered_map& map, const std::str /// Delete raw pointer template -void _DeleteRawPointer(T* ptr) +void ZjDeleteRawPointer(T* ptr) { if (*ptr) { delete *ptr; diff --git a/libs/zj-utility/src/ZjCsvLog.cpp b/libs/zj-utility/src/ZjCsvLog.cpp deleted file mode 100644 index fb833e5..0000000 --- a/libs/zj-utility/src/ZjCsvLog.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @file ZjCsvLog.cpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#include "ZjCsvLog.hpp" -#include "ZjChrono.hpp" -#include "ZjLogMacroExtensions.hpp" -#include "ZjLog.hpp" - -#include "fmt/format.h" -#include "spdlog/sinks/basic_file_sink.h" - -namespace { -static constexpr const char* k_csvLogFolderName {"zj-csv-logs"}; -} - -void ZjCsvLog::ZjCsvLogWorker::init(const std::string& logName, const DataSize dataSize) -{ - // This will take care of spdlog thread initialization, if it's already initialized, it will do nothing - ZjLog::getInstance().init(); - - m_logName = logName; - m_dataSize = dataSize; - _ZJ_THROW_IF(m_logName.empty(), "empty log name"); - _ZJ_THROW_IF(m_dataSize < 1, "invalid data size [{}]", m_dataSize); - - std::string logSaveFolder = ZjLog::getInstance().csvLogFolder().empty() - ? fmt::format("{}/{}", __ZJ_PKG_BUILD_PATH_NO_SLASH__, k_csvLogFolderName) - : fmt::format("{}/{}", ZjLog::getInstance().csvLogFolder(), k_csvLogFolderName); - - m_fileName = fmt::format("{}/{}_{}.csv", logSaveFolder, m_logName, ZjChrono::getTimeIso()); - - _ZJ_TRY(m_logger = spdlog::basic_logger_mt(m_logName, m_fileName, true)); - _ZJ_THROW_IF(!m_logger, "failed to initialize logger [{}]", m_logName); - - /// @note No need to flush every once in a while since the logger is initialized to flush on info level, and all messages are logged at - /// the same level; i.e., they will be logged and flushed at the same time, no information would be lost - /// @note This also means if ZjLog shuts down the spdlog when the whole program terminates, this csv log should have all information - /// already flushed to the file, there shouldn't be any problem - m_logger->flush_on(spdlog::level::info); - m_logger->set_level(spdlog::level::info); - - // https://github.com/gabime/spdlog/wiki/3.-Custom-formatting#pattern-flags - // Formatted as seconds since epoch, dot, microsecond part of the current second - m_logger->set_pattern("%E.%f, %v"); - - _ZJ_INFO("ZjCsvLogWorker starts [{}]", m_logName); -} - -// --------------------------------------------------------- - -void ZjCsvLog::drop(const std::string& logName) -{ - auto it = m_logWorkerMap.find(logName); - - if (it != m_logWorkerMap.end()) { - m_logWorkerMap.erase(it); - } -} - -std::string ZjCsvLog::fileName(const std::string& logName) -{ - if (m_logWorkerMap.find(logName) != m_logWorkerMap.end()) { - return m_logWorkerMap.at(logName).fileName(); - } - - return std::string {}; -} diff --git a/libs/zj-utility/src/ZjProgramSwitch.cpp b/libs/zj-utility/src/ZjProgramSwitch.cpp deleted file mode 100644 index 81c4fea..0000000 --- a/libs/zj-utility/src/ZjProgramSwitch.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file ZjProgramSwitch.cpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#include "ZjProgramSwitch.hpp" - -void ZjProgramSwitch::turnOff() -{ - if (!m_on) { - return; - } - - for (const auto& routine : m_turnOffRoutine) { - if (routine) { - routine(); - } - } - - m_turnOffRoutine.clear(); - m_on = false; -} - -void ZjProgramSwitch::registerTurnOffRoutine(const std::function& routine) -{ - if (!m_on) { - turnOn(); - } - - if (!routine) { - return; - } - - m_turnOffRoutine.emplace_back(routine); -} diff --git a/libs/zj-utility/src/ZjTimer.cpp b/libs/zj-utility/src/ZjTimer.cpp deleted file mode 100644 index d8bea53..0000000 --- a/libs/zj-utility/src/ZjTimer.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file ZjTimer.cpp - * @author Zongyao Jin (zongyaojin@outlook.com) - * @date 2023-08 - * @copyright Copyright (c) 2023 by Zongyao Jin - */ - -#include "ZjTimer.hpp" -#include "ZjLogMacroExtensions.hpp" - -#include - -ZjTimer::ZjTimer(const std::string& name) -{ - if (!name.empty()) { - m_name = fmt::format("ZjTimer-{}", name); - } -} - -void ZjTimer::init(const unsigned frequency, const unsigned overtimeCountLimit) -{ - m_totalCount = 0; - m_totalTimeSpent = 0; - - m_overtimeCountLimit = overtimeCountLimit; - m_overtimeCount = 0; - - m_period = ZjChrono::Ns {static_cast(ZjChrono::s_secToNs / frequency)}; -} - -ZjChrono::Count ZjTimer::guard() -{ - m_totalCount++; - - m_timeSpent = std::chrono::duration_cast(ZjChrono::Clock::now() - m_startTime); - m_totalTimeSpent += m_timeSpent.count(); - m_timeLeft = m_period - m_timeSpent; - - if (m_timeLeft.count() > 0) [[likely]] { - std::this_thread::sleep_for(m_timeLeft); - } else if (m_timeLeft.count() < 0) [[unlikely]] { - m_overtimeCount++; - } - - // There should be an [[unlikely]] here, but the current version of cpplint doesn't get it - if (m_overtimeCountLimit > 0 && m_overtimeCount > m_overtimeCountLimit) { - auto&& loopAvgMs = static_cast(m_totalTimeSpent) / m_totalCount * ZjChrono::s_nsToMs; - auto&& periodMs = m_period.count() * ZjChrono::s_nsToMs; - _ZJ_WARN("[{}] ========== ==========", m_name); - _ZJ_WARN("-- overtime count [{}] exceeds limit [{}]", m_overtimeCount, m_overtimeCountLimit); - _ZJ_WARN("-- loop average [{:.3f} ms], expected period [{:.3f} ms]", loopAvgMs, periodMs); - _ZJ_WARN("-- overtime count has been reset to start over"); - m_overtimeCount = 0; - } - - return m_timeSpent.count(); -} - -double ZjTimer::totalLoopAvg(const ZjChrono::Unit unit) const -{ - switch (unit) { - case ZjChrono::Unit::Sec: - return static_cast(m_totalTimeSpent) / m_totalCount * ZjChrono::s_nsToSec; - case ZjChrono::Unit::Ms: - return static_cast(m_totalTimeSpent) / m_totalCount * ZjChrono::s_nsToMs; - case ZjChrono::Unit::Us: - return static_cast(m_totalTimeSpent) / m_totalCount * ZjChrono::s_nsToUs; - case ZjChrono::Unit::Ns: - return static_cast(m_totalTimeSpent) / m_totalCount; - default: - return 0.0; - } -} - -double ZjTimer::period(const ZjChrono::Unit unit) const -{ - switch (unit) { - case ZjChrono::Unit::Sec: - return m_period.count() * ZjChrono::s_nsToSec; - case ZjChrono::Unit::Ms: - return m_period.count() * ZjChrono::s_nsToMs; - case ZjChrono::Unit::Us: - return m_period.count() * ZjChrono::s_nsToUs; - case ZjChrono::Unit::Ns: - return m_period.count(); - default: - return 0.0; - } -} diff --git a/libs/zj-utility/src/zj-csv-logger.cpp b/libs/zj-utility/src/zj-csv-logger.cpp new file mode 100644 index 0000000..b3f27c2 --- /dev/null +++ b/libs/zj-utility/src/zj-csv-logger.cpp @@ -0,0 +1,71 @@ +/** + * @file zj-csv-logger.cpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#include "zj-csv-logger.hpp" +#include "zj-chrono.hpp" +#include "zj-logging-macros-simplified.hpp" +#include "zj-logger.hpp" + +#include "fmt/format.h" +#include "spdlog/sinks/basic_file_sink.h" + +namespace { +static constexpr const char* k_csvLogFolderName {"zj-csv-logs"}; +} + +void ZjCsvLogger::ZjCsvLoggingUnit::Init(const std::string& log_name, const DataSize data_size) +{ + // This will take care of spdlog thread initialization, if it's already initialized, it will do nothing + ZjLogger::GetInstance().Init(); + + log_name_ = log_name; + data_size_ = data_size; + _ZJ_THROW_IF(log_name_.empty(), "empty log name"); + _ZJ_THROW_IF(data_size_ < 1, "invalid data size [{}]", data_size_); + + std::string logSaveFolder = ZjLogger::GetInstance().CsvLogFolder().empty() + ? fmt::format("{}/{}", __ZJ_PKG_BUILD_PATH_NO_SLASH__, k_csvLogFolderName) + : fmt::format("{}/{}", ZjLogger::GetInstance().CsvLogFolder(), k_csvLogFolderName); + + filename_ = fmt::format("{}/{}_{}.csv", logSaveFolder, log_name_, ZjGetTimeIso()); + + _ZJ_TRY(logger_ = spdlog::basic_logger_mt(log_name_, filename_, true)); + _ZJ_THROW_IF(!logger_, "failed to initialize logger [{}]", log_name_); + + /// @note No need to flush every once in a while since the logger is initialized to flush on info level, and all messages are logged at + /// the same level; i.e., they will be logged and flushed at the same time, no information would be lost + /// @note This also means if ZjLogger shuts down the spdlog when the whole program terminates, this csv log should have all information + /// already flushed to the file, there shouldn't be any problem + logger_->flush_on(spdlog::level::info); + logger_->set_level(spdlog::level::info); + + // https://github.com/gabime/spdlog/wiki/3.-Custom-formatting#pattern-flags + // Formatted as seconds since epoch, dot, microsecond part of the current second + logger_->set_pattern("%E.%f, %v"); + + _ZJ_INFO("ZjCsvLoggingUnit starts [{}]", log_name_); +} + +// --------------------------------------------------------- + +void ZjCsvLogger::Drop(const std::string& log_name) +{ + auto it = logging_units_.find(log_name); + + if (it != logging_units_.end()) { + logging_units_.erase(it); + } +} + +std::string ZjCsvLogger::Filename(const std::string& log_name) +{ + if (logging_units_.find(log_name) != logging_units_.end()) { + return logging_units_.at(log_name).Filename(); + } + + return std::string {}; +} diff --git a/libs/zj-utility/src/zj-program-switch.cpp b/libs/zj-utility/src/zj-program-switch.cpp new file mode 100644 index 0000000..b6afc6f --- /dev/null +++ b/libs/zj-utility/src/zj-program-switch.cpp @@ -0,0 +1,37 @@ +/** + * @file zj-program-switch.cpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#include "zj-program-switch.hpp" + +void ZjProgramSwitch::TurnOff() +{ + if (!on_) { + return; + } + + for (const auto& routine : turn_off_routines_) { + if (routine) { + routine(); + } + } + + turn_off_routines_.clear(); + on_ = false; +} + +void ZjProgramSwitch::RegisterTurnOffRoutine(const std::function& routine) +{ + if (!on_) { + TurnOn(); + } + + if (!routine) { + return; + } + + turn_off_routines_.emplace_back(routine); +} diff --git a/libs/zj-utility/src/zj-timer.cpp b/libs/zj-utility/src/zj-timer.cpp new file mode 100644 index 0000000..90adbc7 --- /dev/null +++ b/libs/zj-utility/src/zj-timer.cpp @@ -0,0 +1,89 @@ +/** + * @file zj-timer.cpp + * @author Zongyao Jin (zongyaojin@outlook.com) + * @date 2023-08 + * @copyright Copyright (c) 2023 by Zongyao Jin + */ + +#include "zj-timer.hpp" +#include "zj-logging-macros-simplified.hpp" + +#include + +ZjTimer::ZjTimer(const std::string& name) +{ + if (!name.empty()) { + name_ = fmt::format("ZjTimer-{}", name); + } +} + +void ZjTimer::Init(const unsigned frequency, const unsigned overtime_count_limit) +{ + total_count_ = 0; + total_time_spent = 0; + + overtime_count_limit_ = overtime_count_limit; + overtime_count_ = 0; + + period_ = ZjChronoNs {static_cast(zj::kSecToNs / frequency)}; +} + +ZjChronoCount ZjTimer::Guard() +{ + total_count_++; + + time_spent_ = std::chrono::duration_cast(ZjChronoClock::now() - start_time_); + total_time_spent += time_spent_.count(); + time_left_ = period_ - time_spent_; + + if (time_left_.count() > 0) [[likely]] { + std::this_thread::sleep_for(time_left_); + } else if (time_left_.count() < 0) [[unlikely]] { + overtime_count_++; + } + + // There should be an [[unlikely]] here, but the current version of cpplint doesn't get it + if (overtime_count_limit_ > 0 && overtime_count_ > overtime_count_limit_) { + auto&& loopAvgMs = static_cast(total_time_spent) / total_count_ * zj::kNsToMs; + auto&& periodMs = period_.count() * zj::kNsToMs; + _ZJ_WARN("[{}] ========== ==========", name_); + _ZJ_WARN("-- overtime count [{}] exceeds limit [{}]", overtime_count_, overtime_count_limit_); + _ZJ_WARN("-- loop average [{:.3f} ms], expected period [{:.3f} ms]", loopAvgMs, periodMs); + _ZJ_WARN("-- overtime count has been reset to start over"); + overtime_count_ = 0; + } + + return time_spent_.count(); +} + +double ZjTimer::TotalLoopAverage(const ZjChronoUnit unit) const +{ + switch (unit) { + case ZjChronoUnit::kSec: + return static_cast(total_time_spent) / total_count_ * zj::kNsToSec; + case ZjChronoUnit::kMs: + return static_cast(total_time_spent) / total_count_ * zj::kNsToMs; + case ZjChronoUnit::kUs: + return static_cast(total_time_spent) / total_count_ * zj::kNsToUs; + case ZjChronoUnit::kNs: + return static_cast(total_time_spent) / total_count_; + default: + return 0.0; + } +} + +double ZjTimer::Period(const ZjChronoUnit unit) const +{ + switch (unit) { + case ZjChronoUnit::kSec: + return period_.count() * zj::kNsToSec; + case ZjChronoUnit::kMs: + return period_.count() * zj::kNsToMs; + case ZjChronoUnit::kUs: + return period_.count() * zj::kNsToUs; + case ZjChronoUnit::kNs: + return period_.count(); + default: + return 0.0; + } +} diff --git a/tests/testZjBasics.cpp b/tests/test-zj-basics.cpp similarity index 91% rename from tests/testZjBasics.cpp rename to tests/test-zj-basics.cpp index ad935df..c63864a 100644 --- a/tests/testZjBasics.cpp +++ b/tests/test-zj-basics.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" #include "zj-utility.hpp" -int testStaticBoolCheckValid() +int TestStaticBoolCheckValid() { _ZJ_STATIC_BOOLEAN_CHECK(true); _ZJ_STATIC_BOOLEAN_CHECK(false); @@ -14,14 +14,14 @@ int testStaticBoolCheckValid() } template -std::string testZjDemangle(T input) +std::string TestZjDemangle(T input) { return _ZJ_DEMANGLE(input); } TEST(TestZjStaticBooleanCheck, ValidCases) { - EXPECT_EQ(testStaticBoolCheckValid(), 1); + EXPECT_EQ(TestStaticBoolCheckValid(), 1); } TEST(TestZjTypeTraits, Demangle) @@ -29,14 +29,14 @@ TEST(TestZjTypeTraits, Demangle) int i {0}; double j {1.2}; float k {2.3}; - ZjFault zjFault; - ZjFailure zjFailure; - - EXPECT_EQ(testZjDemangle(i), "int"); - EXPECT_EQ(testZjDemangle(j), "double"); - EXPECT_EQ(testZjDemangle(k), "float"); - EXPECT_EQ(testZjDemangle(zjFault), "ZjFault"); - EXPECT_EQ(testZjDemangle(zjFailure), "ZjFailure"); + ZjFault zj_fault; + ZjFailure zj_failure; + + EXPECT_EQ(TestZjDemangle(i), "int"); + EXPECT_EQ(TestZjDemangle(j), "double"); + EXPECT_EQ(TestZjDemangle(k), "float"); + EXPECT_EQ(TestZjDemangle(zj_fault), "ZjFault"); + EXPECT_EQ(TestZjDemangle(zj_failure), "ZjFailure"); } TEST(TestZjTypeTraits, TypeId) diff --git a/tests/test-zj-csv-log/main.cpp b/tests/test-zj-csv-log/main.cpp index 1fe941e..a7f5339 100644 --- a/tests/test-zj-csv-log/main.cpp +++ b/tests/test-zj-csv-log/main.cpp @@ -1,7 +1,7 @@ #include "gtest/gtest.h" -#include "ZjCsvLog.hpp" -#include "ZjLogMacroExtensions.hpp" +#include "zj-csv-logger.hpp" +#include "zj-logging-macros-simplified.hpp" #include #include @@ -12,13 +12,13 @@ namespace fs = std::filesystem; -bool printFile(const std::string& fileName) +bool PrintFile(const std::string& Filename) { std::stringstream ss; ss << std::endl << "---------- ---------- ----------" << std::endl; - ss << "Printing file: " << fileName << std::endl; - fs::path file_path(fileName); + ss << "Printing file: " << Filename << std::endl; + fs::path file_path(Filename); // Check if the file exists and is a regular file if (!fs::exists(file_path) || !fs::is_regular_file(file_path)) { @@ -64,35 +64,35 @@ TEST(TestZjCsvLog, TestZjCsvLog) v5 << 40, 30, 20, 10; v6 << 400, 300, 200, 100; - ZjCsvLog::getInstance().log(log1, v1); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 1); - ZjCsvLog::getInstance().log(log1, v2); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 1); - ZjCsvLog::getInstance().log(log1, v3); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 1); + ZjCsvLogger::GetInstance().Log(log1, v1); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 1); + ZjCsvLogger::GetInstance().Log(log1, v2); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 1); + ZjCsvLogger::GetInstance().Log(log1, v3); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 1); - std::string log1_fileName = ZjCsvLog::getInstance().fileName(log1); - _ZJ_DEBUG("log 1 file: [{}]", log1_fileName); + std::string log1_filename = ZjCsvLogger::GetInstance().Filename(log1); + _ZJ_DEBUG("log 1 file: [{}]", log1_filename); - ZjCsvLog::getInstance().log(log2, v4); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 2); + ZjCsvLogger::GetInstance().Log(log2, v4); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 2); - ZjCsvLog::getInstance().drop(log1); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 1); + ZjCsvLogger::GetInstance().Drop(log1); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 1); - ZjCsvLog::getInstance().log(log2, v5); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 1); - ZjCsvLog::getInstance().log(log2, v6); - EXPECT_EQ(ZjCsvLog::getInstance().numLogs(), 1); + ZjCsvLogger::GetInstance().Log(log2, v5); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 1); + ZjCsvLogger::GetInstance().Log(log2, v6); + EXPECT_EQ(ZjCsvLogger::GetInstance().NumLogs(), 1); - std::string log2_fileName = ZjCsvLog::getInstance().fileName(log2); - _ZJ_DEBUG("log 2 file: [{}]", log2_fileName); + std::string log2_filename = ZjCsvLogger::GetInstance().Filename(log2); + _ZJ_DEBUG("log 2 file: [{}]", log2_filename); - EXPECT_TRUE(fs::exists(log1_fileName)); - EXPECT_TRUE(fs::exists(log2_fileName)); + EXPECT_TRUE(fs::exists(log1_filename)); + EXPECT_TRUE(fs::exists(log2_filename)); - EXPECT_TRUE(printFile(log1_fileName)); - EXPECT_TRUE(printFile(log2_fileName)); + EXPECT_TRUE(PrintFile(log1_filename)); + EXPECT_TRUE(PrintFile(log2_filename)); - EXPECT_THROW(ZjCsvLog::getInstance().log(log2, v1), ZjFault); + EXPECT_THROW(ZjCsvLogger::GetInstance().Log(log2, v1), ZjFault); } diff --git a/tests/test-zj-debug-log/main.cpp b/tests/test-zj-debug-log/main.cpp index 5322ed4..22ebab1 100644 --- a/tests/test-zj-debug-log/main.cpp +++ b/tests/test-zj-debug-log/main.cpp @@ -1,9 +1,9 @@ #include "gtest/gtest.h" -#include "zj-log.hpp" +#include "zj-logging.hpp" #include -void testZjThrowIf(const bool flag, const std::string& msg = "") +void TestZjThrowIf(const bool flag, const std::string& msg = "") { if (msg.empty()) { _ZJ_THROW_IF(flag); @@ -12,7 +12,7 @@ void testZjThrowIf(const bool flag, const std::string& msg = "") } } -void testZjThrowFailureIf(const bool flag, const std::string& msg = "") +void TestZjThrowFailureIf(const bool flag, const std::string& msg = "") { if (msg.empty()) { _ZJ_THROW_FAILURE_IF(flag); @@ -21,50 +21,50 @@ void testZjThrowFailureIf(const bool flag, const std::string& msg = "") } } -inline void noThrow() +inline void NoThrow() { _ZJ_INFO("not throwing anything"); } -void throwStdException() +void ThrowStdException() { throw std::runtime_error("bar"); } -void upstreamFault() +void UpstreamFault() { - testZjThrowIf(true, "bar"); + TestZjThrowIf(true, "bar"); } -void upstreamFailure() +void UpstreamFailure() { - testZjThrowFailureIf(true, "bar"); + TestZjThrowFailureIf(true, "bar"); } -void testZjTry(const int type) +void TestZjTry(const int type) { if (type == 1) { - _ZJ_TRY(noThrow()); + _ZJ_TRY(NoThrow()); } else if (type == 2) { - _ZJ_TRY(throwStdException()); + _ZJ_TRY(ThrowStdException()); } else if (type == 3) { - _ZJ_TRY(testZjThrowIf(true, "bar")); + _ZJ_TRY(TestZjThrowIf(true, "bar")); } else if (type == 4) { - _ZJ_TRY(testZjThrowFailureIf(true, "bar")); + _ZJ_TRY(TestZjThrowFailureIf(true, "bar")); } else if (type == 5) { - _ZJ_TRY(upstreamFault()); + _ZJ_TRY(UpstreamFault()); } else if (type == 6) { - _ZJ_TRY(upstreamFailure()); + _ZJ_TRY(UpstreamFailure()); } } -int testZjAssert(const bool flag, const std::string& msg = "") +int TestZjAssert(const bool flag, const std::string& msg = "") { _ZJ_ASSERT(flag, msg); return 1; } -int testZjPrint(const bool flag, const std::string& msg = "") +int TestZjPrint(const bool flag, const std::string& msg = "") { _ZJ_PRINT_IF(flag, msg); return 1; @@ -72,7 +72,7 @@ int testZjPrint(const bool flag, const std::string& msg = "") TEST(TestZjThrow, ThrowExceptions) { - ZjLog::getInstance().init(__ZJ_PKG_BUILD_PATH_NO_SLASH__, __ZJ_PKG_BUILD_PATH_NO_SLASH__); + ZjLogger::GetInstance().Init(__ZJ_PKG_BUILD_PATH_NO_SLASH__, __ZJ_PKG_BUILD_PATH_NO_SLASH__); EXPECT_THROW(_ZJ_THROW(), ZjFault); EXPECT_THROW(_ZJ_THROW("foo"), ZjFault); @@ -80,67 +80,67 @@ TEST(TestZjThrow, ThrowExceptions) EXPECT_THROW(_ZJ_THROW_FAILURE(), ZjFailure); EXPECT_THROW(_ZJ_THROW_FAILURE("foo"), ZjFailure); - auto logFileName = ZjLog::getInstance().fileName(); - _ZJ_INFO("output log file: {}", logFileName); - if (!logFileName.empty()) { - EXPECT_TRUE(std::filesystem::exists(logFileName)); + auto log_filename = ZjLogger::GetInstance().Filename(); + _ZJ_INFO("output log file: {}", log_filename); + if (!log_filename.empty()) { + EXPECT_TRUE(std::filesystem::exists(log_filename)); } } TEST(TestZjThrow, ThrowExceptionsIf) { - EXPECT_THROW(testZjThrowIf(true), ZjFault); - EXPECT_THROW(testZjThrowIf(true, "foo"), ZjFault); + EXPECT_THROW(TestZjThrowIf(true), ZjFault); + EXPECT_THROW(TestZjThrowIf(true, "foo"), ZjFault); - EXPECT_NO_THROW(testZjThrowIf(false)); - EXPECT_NO_THROW(testZjThrowIf(false, "foo")); + EXPECT_NO_THROW(TestZjThrowIf(false)); + EXPECT_NO_THROW(TestZjThrowIf(false, "foo")); - EXPECT_THROW(testZjThrowFailureIf(true), ZjFailure); - EXPECT_THROW(testZjThrowFailureIf(true, "foo"), ZjFailure); + EXPECT_THROW(TestZjThrowFailureIf(true), ZjFailure); + EXPECT_THROW(TestZjThrowFailureIf(true, "foo"), ZjFailure); try { - testZjThrowFailureIf(true, "foo"); + TestZjThrowFailureIf(true, "foo"); } catch (const std::exception& e) { std::cout << "\n\n\n" << e.what() << "\n\n\n" << std::endl; } - EXPECT_NO_THROW(testZjThrowFailureIf(false)); - EXPECT_NO_THROW(testZjThrowFailureIf(false, "foo")); + EXPECT_NO_THROW(TestZjThrowFailureIf(false)); + EXPECT_NO_THROW(TestZjThrowFailureIf(false, "foo")); - auto logFileName = ZjLog::getInstance().fileName(); - _ZJ_INFO("output log file: {}", logFileName); + auto log_filename = ZjLogger::GetInstance().Filename(); + _ZJ_INFO("output log file: {}", log_filename); - if (!logFileName.empty()) { - EXPECT_TRUE(std::filesystem::exists(logFileName)); + if (!log_filename.empty()) { + EXPECT_TRUE(std::filesystem::exists(log_filename)); } } TEST(TestZjTry, TryCatchExceptions) { - EXPECT_NO_THROW(testZjTry(1)); - EXPECT_THROW(testZjTry(2), ZjBug); - EXPECT_THROW(testZjTry(3), ZjFault); - EXPECT_THROW(testZjTry(4), ZjFailure); - EXPECT_THROW(testZjTry(5), ZjFault); - EXPECT_THROW(testZjTry(6), ZjFailure); - - auto logFileName = ZjLog::getInstance().fileName(); - _ZJ_INFO("output log file: {}", logFileName); - - if (!logFileName.empty()) { - EXPECT_TRUE(std::filesystem::exists(logFileName)); + EXPECT_NO_THROW(TestZjTry(1)); + EXPECT_THROW(TestZjTry(2), ZjBug); + EXPECT_THROW(TestZjTry(3), ZjFault); + EXPECT_THROW(TestZjTry(4), ZjFailure); + EXPECT_THROW(TestZjTry(5), ZjFault); + EXPECT_THROW(TestZjTry(6), ZjFailure); + + auto log_filename = ZjLogger::GetInstance().Filename(); + _ZJ_INFO("output log file: {}", log_filename); + + if (!log_filename.empty()) { + EXPECT_TRUE(std::filesystem::exists(log_filename)); } } TEST(TestZjAssert, AssertCases) { - EXPECT_EQ(testZjAssert(true), 1); - EXPECT_EQ(testZjAssert(true, "foo-bar"), 1); + EXPECT_EQ(TestZjAssert(true), 1); + EXPECT_EQ(TestZjAssert(true, "foo-bar"), 1); // https://stackoverflow.com/a/71257678 ::testing::GTEST_FLAG(death_test_style) = "threadsafe"; - ASSERT_DEATH(testZjAssert(false), ""); - ASSERT_DEATH(testZjAssert(false, "asserted false"), ""); + ASSERT_DEATH(TestZjAssert(false), ""); + ASSERT_DEATH(TestZjAssert(false, "asserted false"), ""); } TEST(TestZjPrintIf, PrintCases) @@ -148,10 +148,10 @@ TEST(TestZjPrintIf, PrintCases) bool flag; flag = false; - EXPECT_EQ(testZjPrint(flag), 1); - EXPECT_EQ(testZjPrint(flag, "dummy"), 1); + EXPECT_EQ(TestZjPrint(flag), 1); + EXPECT_EQ(TestZjPrint(flag, "dummy"), 1); flag = true; - EXPECT_EQ(testZjPrint(flag), 1); - EXPECT_EQ(testZjPrint(flag, "printer message"), 1); + EXPECT_EQ(TestZjPrint(flag), 1); + EXPECT_EQ(TestZjPrint(flag, "printer message"), 1); } diff --git a/tests/test-zj-program-switch.cpp b/tests/test-zj-program-switch.cpp new file mode 100644 index 0000000..84c1282 --- /dev/null +++ b/tests/test-zj-program-switch.cpp @@ -0,0 +1,49 @@ +#include "gtest/gtest.h" +#include "zj-program-switch.hpp" + +int globalVar = 10; + +void TestSwitchOff() +{ + ZjProgramSwitch::GetInstance().TurnOff(); +} + +void TestSwitchOn() +{ + ZjProgramSwitch::GetInstance().TurnOn(); +} + +void TestTurnOffRoutine() +{ + globalVar++; +} + +void TestTurnOffRoutine2() +{ + globalVar += 9; +} + +TEST(TestZjProgramSwitch, One) +{ + EXPECT_EQ(ZjProgramSwitch::GetInstance().On(), true); + + ZjProgramSwitch::GetInstance().TurnOn(); + EXPECT_EQ(ZjProgramSwitch::GetInstance().On(), true); + + ZjProgramSwitch::GetInstance().TurnOff(); + EXPECT_EQ(ZjProgramSwitch::GetInstance().On(), false); + + ZjProgramSwitch::GetInstance().RegisterTurnOffRoutine(&TestTurnOffRoutine); + ZjProgramSwitch::GetInstance().RegisterTurnOffRoutine(&TestTurnOffRoutine2); + + EXPECT_EQ(globalVar, 10); + ZjProgramSwitch::GetInstance().TurnOff(); + EXPECT_EQ(ZjProgramSwitch::GetInstance().On(), false); + EXPECT_EQ(globalVar, 20); + + TestSwitchOn(); + EXPECT_EQ(ZjProgramSwitch::GetInstance().On(), true); + TestSwitchOff(); + EXPECT_EQ(ZjProgramSwitch::GetInstance().On(), false); + EXPECT_EQ(globalVar, 20); +} diff --git a/tests/test-zj-singleton.cpp b/tests/test-zj-singleton.cpp new file mode 100644 index 0000000..e1d7ddf --- /dev/null +++ b/tests/test-zj-singleton.cpp @@ -0,0 +1,94 @@ +#include "gtest/gtest.h" +#include "zj-singleton.hpp" + +#include +#include + +class ZjSingletonTest : public ZjSingleton +{ +public: + inline void Add(const int i) { i_ += i; } + inline int GetI() const { return i_; } + +private: + int i_ {0}; +}; + +void addToSingleton(const int i) +{ + ZjSingletonTest::GetInstance().Add(i); +} + +TEST(TestZjSingleton, MultipleAccess) +{ + EXPECT_EQ(ZjSingletonTest::GetInstance().GetI(), 0); + + auto& instance = ZjSingletonTest::GetInstance(); + EXPECT_EQ(instance.GetI(), 0); + + ZjSingletonTest::GetInstance().Add(10); + EXPECT_EQ(ZjSingletonTest::GetInstance().GetI(), 10); + EXPECT_EQ(instance.GetI(), 10); + + addToSingleton(3); + EXPECT_EQ(ZjSingletonTest::GetInstance().GetI(), 13); + EXPECT_EQ(instance.GetI(), 13); +} + +// --------------------------------------------------------- +// --------------------------------------------------------- +// --------------------------------------------------------- + +class ZjSingletonWithInit : public ZjSingleton +{ +public: + inline void Init(const int i) + { + if (!i_) { + i_ = std::make_unique(i); + } + } + + inline void Add(const int i) + { + if (!i_) { + throw std::runtime_error {"not initialized"}; + } + + *i_ += i; + } + + inline int GetI() const + { + if (!i_) { + throw std::runtime_error {"not initialized"}; + } + + return *i_; + } + +private: + std::unique_ptr i_; +}; + +void addToSingletonWithInit(const int i) +{ + ZjSingletonWithInit::GetInstance().Add(i); +} + +TEST(TestZjSingletonWithInit, MultipleAccess) +{ + ZjSingletonWithInit::GetInstance().Init(1000); + EXPECT_EQ(ZjSingletonWithInit::GetInstance().GetI(), 1000); + + auto& instance = ZjSingletonWithInit::GetInstance(); + EXPECT_EQ(instance.GetI(), 1000); + + ZjSingletonWithInit::GetInstance().Add(10); + EXPECT_EQ(ZjSingletonWithInit::GetInstance().GetI(), 1010); + EXPECT_EQ(instance.GetI(), 1010); + + addToSingletonWithInit(3); + EXPECT_EQ(ZjSingletonWithInit::GetInstance().GetI(), 1013); + EXPECT_EQ(instance.GetI(), 1013); +} diff --git a/tests/test-zj-timer.cpp b/tests/test-zj-timer.cpp new file mode 100644 index 0000000..6087688 --- /dev/null +++ b/tests/test-zj-timer.cpp @@ -0,0 +1,62 @@ +#include "gtest/gtest.h" +#include +#include + +#include "zj-timer.hpp" +#include "zj-logging-macros-simplified.hpp" + +ZjChronoCount TestZjTimer(unsigned hz, double sec) +{ + using std::literals::chrono_literals::operator""s; + + auto start = ZjChronoClock::now(); + auto target_ns_count = ZjChronoNs(static_cast(sec * zj::kSecToNs)); + + ZjTimer timer(fmt::format("test: {}hz, {}s", hz, sec)); + timer.Init(hz); + + int actual_total_count = 0; + while (std::chrono::duration_cast(ZjChronoClock::now() - start) < target_ns_count) { + timer.Start(); + std::this_thread::sleep_for(0.0015s); + actual_total_count++; + timer.Guard(); + } + + _ZJ_DEBUG("name: {}, overtime count: {}", timer.Name(), timer.OvertimeCount()); + _ZJ_DEBUG("actual total count vs. expected: [{} | {:.1f}]", actual_total_count, hz * sec); + _ZJ_DEBUG( + "avg loop time vs. period: [{:.5f} s | {:.5f} s]", timer.TotalLoopAverage(ZjChronoUnit::kSec), timer.Period(ZjChronoUnit::kSec)); + return timer.TotalCount(); +} + +TEST(TestZjTimer, One) +{ + EXPECT_NEAR(TestZjTimer(100, 0.1), 10, 1); + ZjLogger::GetInstance().Shutdown(); +} + +TEST(TestZjTimer, Two) +{ + EXPECT_NEAR(TestZjTimer(200, 0.2), 40, 1); + ZjLogger::GetInstance().Shutdown(); +} + +TEST(TestZjTimer, Three) +{ + EXPECT_NEAR(TestZjTimer(300, 0.1), 30, 1); + ZjLogger::GetInstance().Shutdown(); +} + +TEST(TestZjTimer, Four) +{ + // Why the avg loop rate is far lower than 0.002s, but the while loop ran way less than expected; probably due to linux non-realtime + EXPECT_NEAR(TestZjTimer(500, 0.3), 150, 10); + ZjLogger::GetInstance().Shutdown(); +} + +TEST(TestZjTimer, Five) +{ + EXPECT_NEAR(TestZjTimer(30, 0.1), 3, 1); + ZjLogger::GetInstance().Shutdown(); +} diff --git a/tests/test-zj-utility.cpp b/tests/test-zj-utility.cpp new file mode 100644 index 0000000..3d67e4e --- /dev/null +++ b/tests/test-zj-utility.cpp @@ -0,0 +1,61 @@ +#include "gtest/gtest.h" + +#include "zj-utility.hpp" + +#include +#include + +const std::map kMap { + {1.1, 1}, + {2.2, 2}, +}; + +const std::unordered_map kUnorderedMap { + {1.1, 1}, + {2.2, 2}, +}; + +enum class TestType +{ + A = 0, + B, +}; + +const std::map kEnumStringMap { + {TestType::A, "A"}, + {TestType::B, "B"}, +}; + +const std::unordered_map kEnumStringUnorderedMap { + {TestType::A, "A"}, + {TestType::B, "B"}, +}; + +TEST(TestMap, GeneralMaps) +{ + EXPECT_NEAR(ZjGetKeyByValue(kMap, 1), 1.1, std::numeric_limits::min()); + EXPECT_NEAR(ZjGetKeyByValue(kMap, 2), 2.2, std::numeric_limits::min()); + + EXPECT_NEAR(ZjGetKeyByValue(kUnorderedMap, 1), 1.1, std::numeric_limits::min()); + EXPECT_NEAR(ZjGetKeyByValue(kUnorderedMap, 2), 2.2, std::numeric_limits::min()); +} + +TEST(TestMap, EnumStringMaps) +{ + EXPECT_EQ(ZjGetKeyByValue(kEnumStringMap, "A"), TestType::A); + EXPECT_EQ(ZjGetKeyByValue(kEnumStringMap, "B"), TestType::B); + + EXPECT_EQ(ZjGetKeyByValue(kEnumStringUnorderedMap, "A"), TestType::A); + EXPECT_EQ(ZjGetKeyByValue(kEnumStringUnorderedMap, "B"), TestType::B); +} + +TEST(TestDeleteRawPointer, One) +{ + auto* i = new int {10}; + + EXPECT_EQ(*i, 10); + EXPECT_NE(i, nullptr); + + ZjDeleteRawPointer(&i); + EXPECT_EQ(i, nullptr); +} diff --git a/tests/test-zj-verify-numerics/main.cpp b/tests/test-zj-verify-numerics/main.cpp index 2da596c..21551f9 100644 --- a/tests/test-zj-verify-numerics/main.cpp +++ b/tests/test-zj-verify-numerics/main.cpp @@ -1,46 +1,46 @@ #include "gtest/gtest.h" -#include "zj-log.hpp" +#include "zj-logging.hpp" -Eigen::Vector3d invalidEigen() +Eigen::Vector3d InvalidEigen() { Eigen::Vector3d v; v << 1, 2, std::nan("1"); return v; } -double invalidDouble() +double InvalidDouble() { return std::nan("1"); } -void foo(bool flag) +void Foo(bool flag) { if (flag) { - _ZJ_VERIFY(invalidEigen()); + _ZJ_VERIFY(InvalidEigen()); } else { - _ZJ_VERIFY(invalidDouble()); + _ZJ_VERIFY(InvalidDouble()); } } -void bar(bool flag) +void Bar(bool flag) { - _ZJ_TRY(foo(flag)); + _ZJ_TRY(Foo(flag)); } -void hello(bool flag) +void Hello(bool flag) { - _ZJ_TRY(bar(flag)); + _ZJ_TRY(Bar(flag)); } -void world(bool flag) +void World(bool flag) { - _ZJ_TRY(hello(flag)); + _ZJ_TRY(Hello(flag)); } -void noThrow() +void NoThrow() { - invalidEigen(); - invalidDouble(); + InvalidEigen(); + InvalidDouble(); } TEST(TestZjVerify, DirectVerify) @@ -57,11 +57,11 @@ TEST(TestZjVerify, DirectVerify) TEST(TestZjThrow, TraceVerify) { - EXPECT_THROW(_ZJ_TRY(world(true)), ZjSingularity); - EXPECT_THROW(_ZJ_TRY(world(false)), ZjSingularity); + EXPECT_THROW(_ZJ_TRY(World(true)), ZjSingularity); + EXPECT_THROW(_ZJ_TRY(World(false)), ZjSingularity); _ZJ_DEBUG(""); - EXPECT_NO_THROW(_ZJ_TRY(noThrow())); + EXPECT_NO_THROW(_ZJ_TRY(NoThrow())); } TEST(TestZjTry, NoSingular) diff --git a/tests/testZjProgramSwitch.cpp b/tests/testZjProgramSwitch.cpp deleted file mode 100644 index f8445dc..0000000 --- a/tests/testZjProgramSwitch.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "gtest/gtest.h" -#include "ZjProgramSwitch.hpp" - -int globalVar = 10; - -void testSwitchOff() -{ - ZjProgramSwitch::getInstance().turnOff(); -} - -void testSwitchOn() -{ - ZjProgramSwitch::getInstance().turnOn(); -} - -void testTurnOffRoutine() -{ - globalVar++; -} - -void testTurnOffRoutine2() -{ - globalVar += 9; -} - -TEST(TestZjProgramSwitch, One) -{ - EXPECT_EQ(ZjProgramSwitch::getInstance().on(), true); - - ZjProgramSwitch::getInstance().turnOn(); - EXPECT_EQ(ZjProgramSwitch::getInstance().on(), true); - - ZjProgramSwitch::getInstance().turnOff(); - EXPECT_EQ(ZjProgramSwitch::getInstance().on(), false); - - ZjProgramSwitch::getInstance().registerTurnOffRoutine(&testTurnOffRoutine); - ZjProgramSwitch::getInstance().registerTurnOffRoutine(&testTurnOffRoutine2); - - EXPECT_EQ(globalVar, 10); - ZjProgramSwitch::getInstance().turnOff(); - EXPECT_EQ(ZjProgramSwitch::getInstance().on(), false); - EXPECT_EQ(globalVar, 20); - - testSwitchOn(); - EXPECT_EQ(ZjProgramSwitch::getInstance().on(), true); - testSwitchOff(); - EXPECT_EQ(ZjProgramSwitch::getInstance().on(), false); - EXPECT_EQ(globalVar, 20); -} diff --git a/tests/testZjSingleton.cpp b/tests/testZjSingleton.cpp deleted file mode 100644 index 869aa08..0000000 --- a/tests/testZjSingleton.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "gtest/gtest.h" -#include "ZjSingleton.hpp" - -#include -#include - -class ZjSingletonTest : public ZjSingleton -{ -public: - inline void add(const int i) { m_i += i; } - inline int i() const { return m_i; } - -private: - int m_i {0}; -}; - -void addToSingleton(const int i) -{ - ZjSingletonTest::getInstance().add(i); -} - -TEST(TestZjSingleton, MultipleAccess) -{ - EXPECT_EQ(ZjSingletonTest::getInstance().i(), 0); - - auto& instance = ZjSingletonTest::getInstance(); - EXPECT_EQ(instance.i(), 0); - - ZjSingletonTest::getInstance().add(10); - EXPECT_EQ(ZjSingletonTest::getInstance().i(), 10); - EXPECT_EQ(instance.i(), 10); - - addToSingleton(3); - EXPECT_EQ(ZjSingletonTest::getInstance().i(), 13); - EXPECT_EQ(instance.i(), 13); -} - -// --------------------------------------------------------- -// --------------------------------------------------------- -// --------------------------------------------------------- - -class ZjSingletonWithInit : public ZjSingleton -{ -public: - inline void init(const int i) - { - if (!m_i) { - m_i = std::make_unique(i); - } - } - - inline void add(const int i) - { - if (!m_i) { - throw std::runtime_error {"not initialized"}; - } - - *m_i += i; - } - - inline int i() const - { - if (!m_i) { - throw std::runtime_error {"not initialized"}; - } - - return *m_i; - } - -private: - std::unique_ptr m_i; -}; - -void addToSingletonWithInit(const int i) -{ - ZjSingletonWithInit::getInstance().add(i); -} - -TEST(TestZjSingletonWithInit, MultipleAccess) -{ - ZjSingletonWithInit::getInstance().init(1000); - EXPECT_EQ(ZjSingletonWithInit::getInstance().i(), 1000); - - auto& instance = ZjSingletonWithInit::getInstance(); - EXPECT_EQ(instance.i(), 1000); - - ZjSingletonWithInit::getInstance().add(10); - EXPECT_EQ(ZjSingletonWithInit::getInstance().i(), 1010); - EXPECT_EQ(instance.i(), 1010); - - addToSingletonWithInit(3); - EXPECT_EQ(ZjSingletonWithInit::getInstance().i(), 1013); - EXPECT_EQ(instance.i(), 1013); -} diff --git a/tests/testZjTimer.cpp b/tests/testZjTimer.cpp deleted file mode 100644 index 7458f4c..0000000 --- a/tests/testZjTimer.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "gtest/gtest.h" -#include -#include - -#include "ZjTimer.hpp" -#include "ZjLogMacroExtensions.hpp" - -ZjChrono::Count testZjTimer(unsigned hz, double sec) -{ - using std::literals::chrono_literals::operator""s; - - auto start = ZjChrono::Clock::now(); - auto targetNsCount = ZjChrono::Ns(static_cast(sec * ZjChrono::s_secToNs)); - - ZjTimer timer(fmt::format("test: {}hz, {}s", hz, sec)); - timer.init(hz); - - int actualTotalCount = 0; - while (std::chrono::duration_cast(ZjChrono::Clock::now() - start) < targetNsCount) { - timer.start(); - std::this_thread::sleep_for(0.0015s); - actualTotalCount++; - timer.guard(); - } - - _ZJ_DEBUG("name: {}, overtime count: {}", timer.name(), timer.overtimeCount()); - _ZJ_DEBUG("actual total count vs. expected: [{} | {:.1f}]", actualTotalCount, hz * sec); - _ZJ_DEBUG( - "avg loop time vs. period: [{:.5f} s | {:.5f} s]", timer.totalLoopAvg(ZjChrono::Unit::Sec), timer.period(ZjChrono::Unit::Sec)); - return timer.totalCount(); -} - -TEST(TestZjTimer, One) -{ - EXPECT_NEAR(testZjTimer(100, 0.1), 10, 1); - ZjLog::getInstance().shutdown(); -} - -TEST(TestZjTimer, Two) -{ - EXPECT_NEAR(testZjTimer(200, 0.2), 40, 1); - ZjLog::getInstance().shutdown(); -} - -TEST(TestZjTimer, Three) -{ - EXPECT_NEAR(testZjTimer(300, 0.1), 30, 1); - ZjLog::getInstance().shutdown(); -} - -TEST(TestZjTimer, Four) -{ - // Why the avg loop rate is far lower than 0.002s, but the while loop ran way less than expected; probably due to linux non-realtime - EXPECT_NEAR(testZjTimer(500, 0.3), 150, 10); - ZjLog::getInstance().shutdown(); -} - -TEST(TestZjTimer, Five) -{ - EXPECT_NEAR(testZjTimer(30, 0.1), 3, 1); - ZjLog::getInstance().shutdown(); -} diff --git a/tests/testZjUtility.cpp b/tests/testZjUtility.cpp deleted file mode 100644 index 7c9836a..0000000 --- a/tests/testZjUtility.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "gtest/gtest.h" - -#include "ZjUtility.hpp" - -#include -#include - -const std::map k_map { - {1.1, 1}, - {2.2, 2}, -}; - -const std::unordered_map k_unorderedMap { - {1.1, 1}, - {2.2, 2}, -}; - -enum class TestType -{ - A = 0, - B, -}; - -const std::map k_enumStringMap { - {TestType::A, "A"}, - {TestType::B, "B"}, -}; - -const std::unordered_map k_enumStringUnorderedMap { - {TestType::A, "A"}, - {TestType::B, "B"}, -}; - -TEST(TestMap, GeneralMaps) -{ - EXPECT_NEAR(_GetKeyByValue(k_map, 1), 1.1, std::numeric_limits::min()); - EXPECT_NEAR(_GetKeyByValue(k_map, 2), 2.2, std::numeric_limits::min()); - - EXPECT_NEAR(_GetKeyByValue(k_unorderedMap, 1), 1.1, std::numeric_limits::min()); - EXPECT_NEAR(_GetKeyByValue(k_unorderedMap, 2), 2.2, std::numeric_limits::min()); -} - -TEST(TestMap, EnumStringMaps) -{ - EXPECT_EQ(_GetKeyByValue(k_enumStringMap, "A"), TestType::A); - EXPECT_EQ(_GetKeyByValue(k_enumStringMap, "B"), TestType::B); - - EXPECT_EQ(_GetKeyByValue(k_enumStringUnorderedMap, "A"), TestType::A); - EXPECT_EQ(_GetKeyByValue(k_enumStringUnorderedMap, "B"), TestType::B); -} - -TEST(TestDeleteRawPointer, One) -{ - auto* i = new int {10}; - - EXPECT_EQ(*i, 10); - EXPECT_NE(i, nullptr); - - _DeleteRawPointer(&i); - EXPECT_EQ(i, nullptr); -}