Skip to content

Commit

Permalink
Adds format support for logging.
Browse files Browse the repository at this point in the history
This makes it possible to use the C++20 formatting library next to the
existing stream operator when creating log messages. This feature is
only available when the standard library used has format support.
  • Loading branch information
mordante committed Jul 5, 2023
1 parent c4680e9 commit afdc194
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 1 deletion.
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ All tests passed (2 asserts in 2 tests)
<details open><summary>&nbsp;&nbsp;&nbsp;&nbsp;Misc</summary>
<p>
<details open><summary>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Logging</summary>
<details open><summary>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Logging using streams</summary>
<p>
```cpp
Expand Down Expand Up @@ -1053,6 +1053,36 @@ asserts: 1 | 0 passed | 1 failed
</p>
</details>

<details open><summary>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Logging using formatting</summary>
<p>
This requires using C++20 with a standard library with std::format support.

```cpp
"logging"_test = [] {
log("\npre {} == {}\n", 42, 43);
expect(42_i == 43) << "message on failure";
log("\npost {} == {} -> {}\n", 42, 43, 42 == 43);
};
```

```
Running "logging"...
pre 42 == 43
logging.cpp:8:FAILED [42 == 43] message on failure
post 42 == 43 -> false
FAILED
===============================================================================
tests: 1 | 1 failed
asserts: 1 | 0 passed | 1 failed
```

> https://godbolt.org/z/26fPSY
</p>
</details>

<details open><summary>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Matchers</summary>
<p>

Expand Down
8 changes: 8 additions & 0 deletions example/log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@ int main() {
expect(e.value() == true);
};
#endif

#if defined(BOOST_UT_HAS_FORMAT)
"log format"_test = [] {
boost::ut::log("\npre: {}\n", 42);
expect(42_i == 42) << "message on failure";
boost::ut::log("\npost\n");
};
#endif
}
22 changes: 22 additions & 0 deletions include/boost/ut.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ export import std;
#include <iso646.h> // and, or, not, ...
#endif

#include <version>
// Before libc++ 17 had experimental support for format and it required a
// special build flag. Currently libc++ has not implemented all C++20 chrono
// improvements. Therefore doesn't define __cpp_lib_format, instead query the
// library version to detect the support status.
//
// MSVC STL and libstdc++ provide __cpp_lib_format.
#if defined(__cpp_lib_format) or (defined(_LIBCPP_VERSION) and _LIBCPP_VERSION >= 170000)
#define BOOST_UT_HAS_FORMAT
#endif

#if not defined(__cpp_rvalue_references)
#error "[Boost::ext].UT requires support for rvalue references";
#elif not defined(__cpp_decltype)
Expand Down Expand Up @@ -80,6 +91,9 @@ export import std;
#include <exception>
#endif

#if __has_include(<format>)
#include <format>
#endif
#if __has_include(<source_location>)
#include <source_location>
#endif
Expand Down Expand Up @@ -2249,6 +2263,14 @@ struct log {
on<TMsg>(events::log{msg});
return next{};
}

#if defined(BOOST_UT_HAS_FORMAT)
template <class... Args>
void operator()(std::format_string<Args...> fmt, Args&&... args) {
on<std::string>(
events::log{std::vformat(fmt.get(), std::make_format_args(args...))});
}
#endif
};

template <class TExpr>
Expand Down
19 changes: 19 additions & 0 deletions test/ut/ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include <string_view>
#include <vector>

#if __has_include(<format>)
#include <format>
#endif

namespace ut = boost::ut;

namespace test_has_member_object {
Expand Down Expand Up @@ -895,7 +899,22 @@ int main() {
test_assert(' ' == std::any_cast<char>(test_cfg.log_calls[6]));
test_assert(42 == std::any_cast<int>(test_cfg.log_calls[7]));
}
#if defined(BOOST_UT_HAS_FORMAT)
{
test_cfg = fake_cfg{};

"logging with format"_test = [] {
boost::ut::log("{:<10} {:#x}\n{:<10} {}\n", "expected:", 42,
std::string("actual:"), 99);
};

test_assert(1 == std::size(test_cfg.run_calls));
test_assert("logging with format"sv == test_cfg.run_calls[0].name);
test_assert(1 == std::size(test_cfg.log_calls));
test_assert("expected: 0x2a\nactual: 99\n"sv ==
std::any_cast<std::string>(test_cfg.log_calls[0]));
}
#endif
{
test_cfg = fake_cfg{};
expect(true) << "true msg" << true;
Expand Down

0 comments on commit afdc194

Please sign in to comment.