From 1745660582dd9ba6d5db6ef31dbe1c0f9d55e9f4 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Sat, 11 Jan 2025 15:54:52 -0700 Subject: [PATCH 1/2] :arrow_up: Update stdx Problem: - `stdx` added a breaking change, viz. the ct_string format result always being returned from `ct_format`. Solution: - Update code to deal with that. --- CMakeLists.txt | 2 +- include/cib/detail/runtime_conditional.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 843885f6..2482b1e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ include(cmake/string_catalog.cmake) add_versioned_package("gh:boostorg/mp11#boost-1.83.0") fmt_recipe(10.2.1) add_versioned_package("gh:intel/cpp-baremetal-concurrency#7c5b26c") -add_versioned_package("gh:intel/cpp-std-extensions#4d57b2e") +add_versioned_package("gh:intel/cpp-std-extensions#2834680") add_versioned_package("gh:intel/cpp-baremetal-senders-and-receivers#73d95bc") set(GEN_STR_CATALOG diff --git a/include/cib/detail/runtime_conditional.hpp b/include/cib/detail/runtime_conditional.hpp index d18a9277..238d271f 100644 --- a/include/cib/detail/runtime_conditional.hpp +++ b/include/cib/detail/runtime_conditional.hpp @@ -85,7 +85,7 @@ operator and(runtime_condition const &lhs, constexpr auto name = stdx::ct_format<"{} and {}">(CX_VALUE(LhsName), CX_VALUE(RhsName)); - return runtime_condition{}; + return runtime_condition{}; } } From de2b4ae5ab6f31daa89ca8d6140ebdbde78bd19a Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Sat, 11 Jan 2025 15:53:28 -0700 Subject: [PATCH 2/2] :sparkles: Introduce log environment Problem: - Logging is parameterized on many axes. Most logging systems include the level. Some also include a module or source. Some include other parameters. - Logging machinery should be agnostic to the parameterizations of each logging backend. Adding extra parameters to logging macros is not scalable. Solution: - Expand the current logging module to the idea of a logging environment. An environment allows compile-time scope-based arguments to be passed through the logging machinery; logging backends can extract whichever values they can use from the current environment. --- include/log/catalog/mipi_encoder.hpp | 21 ++++--- include/log/env.hpp | 91 ++++++++++++++++++++++++++++ include/log/fmt/logger.hpp | 6 +- include/log/log.hpp | 31 +++------- include/log/module.hpp | 27 +++++++++ test/log/CMakeLists.txt | 2 +- test/log/catalog1_lib.cpp | 10 +-- test/log/catalog2a_lib.cpp | 2 +- test/log/catalog2b_lib.cpp | 2 +- test/log/env.cpp | 50 +++++++++++++++ test/log/mipi_encoder.cpp | 41 ++++++------- test/log/module_id.cpp | 20 +++--- 12 files changed, 230 insertions(+), 73 deletions(-) create mode 100644 include/log/env.hpp create mode 100644 include/log/module.hpp create mode 100644 test/log/env.cpp diff --git a/include/log/catalog/mipi_encoder.hpp b/include/log/catalog/mipi_encoder.hpp index ffea4d82..4a761bcc 100644 --- a/include/log/catalog/mipi_encoder.hpp +++ b/include/log/catalog/mipi_encoder.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include namespace logging::mipi { @@ -28,11 +30,10 @@ constexpr auto to_message() { }(std::make_integer_sequence{}); } -template constexpr auto to_module() { - constexpr auto s = S::value; - using char_t = typename std::remove_cv_t::value_type; +template constexpr auto to_module() { + constexpr auto s = std::string_view{S}; return [&](std::integer_sequence) { - return sc::module_string>{}; + return sc::module_string>{}; }(std::make_integer_sequence{}); } } // namespace detail @@ -102,19 +103,19 @@ using catalog_msg_t = template struct log_handler { constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {} - template + template ALWAYS_INLINE auto log(FilenameStringType, LineNumberType, MsgType const &msg) -> void { - log_msg(msg); + log_msg(msg); } - template + template ALWAYS_INLINE auto log_msg(Msg msg) -> void { msg.apply([&](S, Args... args) { using Message = decltype(detail::to_message()); - using Module = decltype(detail::to_module()); + using Module = + decltype(detail::to_module()); dispatch_message(catalog(), module(), static_cast(args)...); }); diff --git a/include/log/env.hpp b/include/log/env.hpp new file mode 100644 index 00000000..265828fb --- /dev/null +++ b/include/log/env.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __clang__ +#define CIB_PRAGMA_SEMI +#else +#define CIB_PRAGMA_SEMI ; +#endif + +namespace logging { +template struct prop { + [[nodiscard]] CONSTEVAL static auto query(decltype(Query)) noexcept { + return Value; + } +}; + +namespace detail { +template +concept valid_query_for = requires { Env::query(Q{}); }; + +template +concept valid_query_over = (... or valid_query_for); + +template struct has_query { + template + using fn = std::bool_constant>; +}; +} // namespace detail + +template struct env { + template Q> + CONSTEVAL static auto query(Q) noexcept { + using I = boost::mp11::mp_find_if_q, + detail::has_query>; + using E = boost::mp11::mp_at, I>; + return Q{}(E{}); + } +}; + +namespace detail { +template struct autowrap { + CONSTEVAL autowrap(T t) : value(t) {} + T value; +}; + +template using str_lit_t = char const (&)[N]; + +template struct autowrap> { + CONSTEVAL autowrap(str_lit_t str) : value(str) {} + stdx::ct_string value; +}; + +template autowrap(T) -> autowrap; +template autowrap(str_lit_t) -> autowrap>; + +template struct wrap { + constexpr static auto value = V; +}; + +template struct for_each_pair; +template struct for_each_pair> { + template + using type = env< + prop...>, + 2 * Is>::value.value, + stdx::ct...>, + 2 * Is + 1>::value.value>()>...>; +}; +} // namespace detail +} // namespace logging + +using cib_log_env_t = logging::env<>; + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define CIB_LOG_ENV(...) \ + STDX_PRAGMA(diagnostic push) \ + STDX_PRAGMA(diagnostic ignored "-Wshadow") \ + using cib_log_env_t [[maybe_unused]] = \ + decltype([] { \ + using new_env_t = typename logging::detail::for_each_pair< \ + std::make_index_sequence>::template type; \ + return boost::mp11::mp_append{}; \ + }.template operator()<__VA_ARGS__>()) CIB_PRAGMA_SEMI \ + STDX_PRAGMA(diagnostic pop) +// NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/include/log/fmt/logger.hpp b/include/log/fmt/logger.hpp index 9f4f3e72..255f4f57 100644 --- a/include/log/fmt/logger.hpp +++ b/include/log/fmt/logger.hpp @@ -1,7 +1,9 @@ #pragma once #include +#include +#include #include #include @@ -25,7 +27,7 @@ namespace logging::fmt { template struct log_handler { constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {} - template auto log(FilenameStringType, LineNumberType, MsgType const &msg) -> void { auto const currentTime = @@ -36,7 +38,7 @@ template struct log_handler { stdx::for_each( [&](auto &out) { ::fmt::format_to(out, "{:>8}us {} [{}]: ", currentTime, - level_constant{}, ModuleId::value); + level_constant{}, get_module(Env{}).value); msg.apply( [&](StringType, auto const &...args) { ::fmt::format_to(out, StringType::value, args...); diff --git a/include/log/log.hpp b/include/log/log.hpp index ac74d2f6..d5afc77a 100644 --- a/include/log/log.hpp +++ b/include/log/log.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include #include @@ -44,37 +46,18 @@ ALWAYS_INLINE constexpr static auto get_config() -> auto & { } } -template ALWAYS_INLINE static auto log(TArgs &&...args) -> void { auto &cfg = get_config(); - cfg.logger.template log(std::forward(args)...); + cfg.logger.template log(std::forward(args)...); } - -template struct module_id_t { - using type = decltype(stdx::ct_string_to_type()); -}; } // namespace logging -using cib_log_module_id_t = typename logging::module_id_t<"default">::type; - // NOLINTBEGIN(cppcoreguidelines-macro-usage) -#ifdef __clang__ -#define CIB_PRAGMA_SEMI -#else -#define CIB_PRAGMA_SEMI ; -#endif - -#define CIB_LOG_MODULE(S) \ - STDX_PRAGMA(diagnostic push) \ - STDX_PRAGMA(diagnostic ignored "-Wshadow") \ - using cib_log_module_id_t [[maybe_unused]] = \ - typename logging::module_id_t::type CIB_PRAGMA_SEMI STDX_PRAGMA( \ - diagnostic pop) - #define CIB_LOG(FLAVOR, LEVEL, MSG, ...) \ - logging::log( \ + logging::log( \ __FILE__, __LINE__, sc::format(MSG##_sc __VA_OPT__(, ) __VA_ARGS__)) #define CIB_TRACE(...) \ @@ -90,7 +73,7 @@ using cib_log_module_id_t = typename logging::module_id_t<"default">::type; [] { \ constexpr auto str = sc::format(MSG##_sc __VA_OPT__(, ) __VA_ARGS__); \ logging::log(__FILE__, __LINE__, str); \ + cib_log_env_t>(__FILE__, __LINE__, str); \ str.apply([](S s, Args... args) { \ constexpr auto cts = stdx::ct_string_from_type(s); \ stdx::panic(args...); \ @@ -111,7 +94,7 @@ ALWAYS_INLINE static auto log_version() -> void { }) { l_cfg.logger.template log_build(); } else { - l_cfg.logger.template log( + l_cfg.logger.template log( "", 0, sc::format("Version: {} ({})"_sc, sc::uint_, stdx::ct_string_to_type + +#include + +#include + +namespace logging { +[[maybe_unused]] constexpr inline struct get_module_t { + template + requires true // more constrained + CONSTEVAL auto operator()(T &&t) const noexcept( + noexcept(std::forward(t).query(std::declval()))) + -> decltype(std::forward(t).query(*this)) { + return std::forward(t).query(*this); + } + + CONSTEVAL auto operator()(auto &&) const { + using namespace stdx::literals; + return "default"_ctst; + } +} get_module; +} // namespace logging + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CIB_LOG_MODULE(S) CIB_LOG_ENV(logging::get_module, S) diff --git a/test/log/CMakeLists.txt b/test/log/CMakeLists.txt index cfa5924d..216f9acc 100644 --- a/test/log/CMakeLists.txt +++ b/test/log/CMakeLists.txt @@ -1,4 +1,4 @@ -add_tests(FILES log module_id LIBRARIES cib_log) +add_tests(FILES log module_id env LIBRARIES cib_log) add_tests(FILES fmt_logger LIBRARIES cib_log_fmt) add_tests(FILES mipi_encoder mipi_logger LIBRARIES cib_log_mipi) diff --git a/test/log/catalog1_lib.cpp b/test/log/catalog1_lib.cpp index c4a4f90e..1243ca7c 100644 --- a/test/log/catalog1_lib.cpp +++ b/test/log/catalog1_lib.cpp @@ -27,32 +27,32 @@ auto log_with_fixed_module_id() -> void; auto log_zero_args() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( "A string with no placeholders"_sc); } auto log_one_ct_arg() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("B string with {} placeholder"_sc, "one"_sc)); } auto log_one_rt_arg() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("C string with {} placeholder"_sc, 1)); } auto log_with_non_default_module_id() -> void { CIB_LOG_MODULE("not default"); auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("ModuleID string with {} placeholder"_sc, 1)); } auto log_with_fixed_module_id() -> void { CIB_LOG_MODULE("fixed"); auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("Fixed ModuleID string with {} placeholder"_sc, 1)); } diff --git a/test/log/catalog2a_lib.cpp b/test/log/catalog2a_lib.cpp index 5c7d2c0d..16be2a65 100644 --- a/test/log/catalog2a_lib.cpp +++ b/test/log/catalog2a_lib.cpp @@ -20,6 +20,6 @@ auto log_two_rt_args() -> void; auto log_two_rt_args() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("D string with {} and {} placeholder"_sc, 1, 2)); } diff --git a/test/log/catalog2b_lib.cpp b/test/log/catalog2b_lib.cpp index 18430b6c..2887d076 100644 --- a/test/log/catalog2b_lib.cpp +++ b/test/log/catalog2b_lib.cpp @@ -21,6 +21,6 @@ auto log_rt_enum_arg() -> void; auto log_rt_enum_arg() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; using namespace ns; - cfg.logger.log_msg( + cfg.logger.log_msg( format("E string with {} placeholder"_sc, E::value)); } diff --git a/test/log/env.cpp b/test/log/env.cpp new file mode 100644 index 00000000..0eb62d2b --- /dev/null +++ b/test/log/env.cpp @@ -0,0 +1,50 @@ +#include +#include + +#include + +namespace { +[[maybe_unused]] constexpr inline struct custom_t { + template + requires true // more constrained + CONSTEVAL auto operator()(T &&t) const + noexcept(noexcept(std::forward(t).query(std::declval()))) + -> decltype(std::forward(t).query(*this)) { + return std::forward(t).query(*this); + } + + CONSTEVAL auto operator()(auto &&) const { + using namespace stdx::literals; + return 42; + } +} custom; +} // namespace + +TEST_CASE("override environment", "[log_env]") { + static_assert(custom(cib_log_env_t{}) == 42); + CIB_LOG_ENV(custom, 1); + static_assert(custom(cib_log_env_t{}) == 1); + { + CIB_LOG_ENV(custom, 2); + static_assert(custom(cib_log_env_t{}) == 2); + } +} + +TEST_CASE("supplement environment", "[log_env]") { + CIB_LOG_ENV(custom, 1); + static_assert(custom(cib_log_env_t{}) == 1); + { + using namespace stdx::literals; + CIB_LOG_ENV(logging::get_module, "hello"); + static_assert(custom(cib_log_env_t{}) == 1); + static_assert(logging::get_module(cib_log_env_t{}) == "hello"_ctst); + } +} + +TEST_CASE("multi-value environment", "[log_env]") { + CIB_LOG_ENV(custom, 1, logging::get_module, "hello"); + + using namespace stdx::literals; + static_assert(custom(cib_log_env_t{}) == 1); + static_assert(logging::get_module(cib_log_env_t{}) == "hello"_ctst); +} diff --git a/test/log/mipi_encoder.cpp b/test/log/mipi_encoder.cpp index 4a08a2b1..cefb2b7a 100644 --- a/test/log/mipi_encoder.cpp +++ b/test/log/mipi_encoder.cpp @@ -132,19 +132,18 @@ template <> inline auto conc::injected_policy<> = test_conc_policy{}; TEST_CASE("log zero arguments", "[mipi]") { test_critical_section::count = 0; - auto cfg = - logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg("Hello"_sc); + auto cfg = logging::mipi::config{ + test_log_args_destination{}}; + cfg.logger.log_msg("Hello"_sc); CHECK(test_critical_section::count == 2); } TEST_CASE("log one argument", "[mipi]") { test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}}; - cfg.logger.log_msg( + test_log_args_destination{}}; + cfg.logger.log_msg( format("{}"_sc, 17u)); CHECK(test_critical_section::count == 2); } @@ -152,9 +151,9 @@ TEST_CASE("log one argument", "[mipi]") { TEST_CASE("log two arguments", "[mipi]") { test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}}; - cfg.logger.log_msg( + test_log_args_destination{}}; + cfg.logger.log_msg( format("{} {}"_sc, 17u, 18u)); CHECK(test_critical_section::count == 2); } @@ -163,18 +162,18 @@ TEST_CASE("log more than two arguments", "[mipi]") { { test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_buf_destination{}}; - cfg.logger.log_msg( + test_log_buf_destination{}}; + cfg.logger.log_msg( format("{} {} {}"_sc, 17u, 18u, 19u)); CHECK(test_critical_section::count == 2); } { test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_buf_destination{}}; - cfg.logger.log_msg( + test_log_buf_destination{}}; + cfg.logger.log_msg( format("{} {} {} {}"_sc, 17u, 18u, 19u, 20u)); CHECK(test_critical_section::count == 2); } @@ -184,11 +183,11 @@ TEST_CASE("log to multiple destinations", "[mipi]") { test_critical_section::count = 0; num_log_args_calls = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}, - test_log_args_destination{}}; - cfg.logger.log_msg( + test_log_args_destination{}, + test_log_args_destination{}}; + cfg.logger.log_msg( format("{} {}"_sc, 17u, 18u)); CHECK(test_critical_section::count == 4); CHECK(num_log_args_calls == 2); diff --git a/test/log/module_id.cpp b/test/log/module_id.cpp index 883e3f7b..8b99411f 100644 --- a/test/log/module_id.cpp +++ b/test/log/module_id.cpp @@ -8,23 +8,26 @@ #include TEST_CASE("default log module id", "[module_id]") { - static_assert(std::is_same_v); + using namespace stdx::literals; + static_assert(logging::get_module(cib_log_env_t{}) == "default"_ctst); } namespace ns { CIB_LOG_MODULE("ns"); TEST_CASE("log module id overridden at namespace scope", "[module_id]") { - static_assert(std::is_same_v); + using namespace stdx::literals; + static_assert(logging::get_module(cib_log_env_t{}) == "ns"_ctst); } } // namespace ns struct S { - CIB_LOG_MODULE("S"); - template constexpr static auto test() { - static_assert(std::is_same_v); + using namespace stdx::literals; + static_assert(logging::get_module(cib_log_env_t{}) == "S"_ctst); } + + CIB_LOG_MODULE("S"); }; TEST_CASE("log module id overridden at class scope", "[module_id]") { @@ -33,7 +36,8 @@ TEST_CASE("log module id overridden at class scope", "[module_id]") { template constexpr static auto func_test() { CIB_LOG_MODULE("fn"); - static_assert(std::is_same_v); + using namespace stdx::literals; + static_assert(logging::get_module(cib_log_env_t{}) == "fn"_ctst); } TEST_CASE("log module id overridden at function scope", "[module_id]") { @@ -45,7 +49,7 @@ TEST_CASE("log module id overridden at statement scope", "[module_id]") { { CIB_LOG_MODULE("statement"); - static_assert( - std::is_same_v); + using namespace stdx::literals; + static_assert(logging::get_module(cib_log_env_t{}) == "statement"_ctst); } }