Skip to content

Commit

Permalink
better char array support
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuoqiang authored and odygrd committed Oct 31, 2023
1 parent ccd14b7 commit ebf276a
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 4 deletions.
13 changes: 13 additions & 0 deletions examples/example_trivial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ int main()
std::array<uint32_t, 4> arr = {1, 2, 3, 4};
LOG_INFO(logger, "This is a log info example {}", arr);

union
{
char no_0[2];
char mid_0[6]{'1', '2', '3', '4', '\0', 6};
} char_arrays;

// only output "12" even if there's no '\0' at the end
LOG_INFO(logger, R"(This is a log info example for char array without '\0': {})", char_arrays.no_0);

// output "1234" until the '\0'
LOG_INFO(logger, R"(This is a log info example for char array with '\0' in middle: {})",
char_arrays.mid_0);

// Using a dynamic runtime log level
std::array<quill::LogLevel, 4> const runtime_log_levels = {
quill::LogLevel::Debug, quill::LogLevel::Info, quill::LogLevel::Warning, quill::LogLevel::Error};
Expand Down
48 changes: 46 additions & 2 deletions quill/include/quill/detail/Serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

#pragma once

#ifndef __STDC_WANT_LIB_EXT1__
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#endif

#include "misc/Utilities.h"
#include "quill/LogLevel.h"
#include "quill/MacroMetadata.h"
Expand All @@ -23,9 +28,25 @@ namespace quill
namespace detail
{

constexpr auto strnlen =
#ifdef __STDC_LIB_EXT1__
::strnlen_s
#else
::strnlen
#endif
;

/** Forward declaration **/
class LoggerDetails;

template <typename Arg>
QUILL_NODISCARD constexpr bool is_type_of_c_array()
{
using ArgType = detail::remove_cvref_t<Arg>;
return std::is_array<ArgType>::value &&
std::is_same<detail::remove_cvref_t<typename std::remove_extent<ArgType>::type>, char>::value;
}

template <typename Arg>
QUILL_NODISCARD constexpr bool is_type_of_c_string()
{
Expand Down Expand Up @@ -177,7 +198,13 @@ template <size_t CstringIdx, typename Arg, typename... Args>
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT constexpr size_t get_args_sizes(size_t* c_string_sizes,
Arg const& arg, Args const&... args)
{
if constexpr (is_type_of_c_string<Arg>())
if constexpr (is_type_of_c_array<Arg>())
{
size_t const len = strnlen(arg, detail::array_size_v<Arg>) + 1;
c_string_sizes[CstringIdx] = len;
return len + get_args_sizes<CstringIdx + 1>(c_string_sizes, args...);
}
else if constexpr (is_type_of_c_string<Arg>())
{
size_t const len = strlen(arg) + 1;
c_string_sizes[CstringIdx] = len;
Expand Down Expand Up @@ -225,7 +252,24 @@ template <size_t CstringIdx, typename Arg, typename... Args>
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT constexpr std::byte* encode_args(size_t* c_string_sizes, std::byte* out,
Arg&& arg, Args&&... args)
{
if constexpr (is_type_of_c_string<Arg>())
if constexpr (is_type_of_c_array<Arg>())
{
const auto size = c_string_sizes[CstringIdx];
constexpr auto array_size = detail::array_size_v<Arg>;
if (QUILL_UNLIKELY(size > array_size))
{
// no '\0' in c array
assert(size == array_size + 1);
std::memcpy(out, arg, array_size);
out[size - 1] = std::byte{'\0'};
}
else
{
std::memcpy(out, arg, size);
}
return encode_args<CstringIdx + 1>(c_string_sizes, out + size, std::forward<Args>(args)...);
}
else if constexpr (is_type_of_c_string<Arg>())
{
std::memcpy(out, arg, c_string_sizes[CstringIdx]);
return encode_args<CstringIdx + 1>(c_string_sizes, out + c_string_sizes[CstringIdx],
Expand Down
3 changes: 3 additions & 0 deletions quill/include/quill/detail/misc/TypeTraitsCopyable.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ struct remove_cvref
template< class T >
using remove_cvref_t = typename remove_cvref<T>::type;

template <typename Arg>
constexpr size_t array_size_v = std::extent<remove_cvref_t<Arg>>::value;

/**
* fmtquill::streamed detection
*/
Expand Down
60 changes: 59 additions & 1 deletion quill/test/LogTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,64 @@ TEST_CASE("default_logger_ints_and_large_string")
quill::detail::remove_file(filename);
}

/***/
TEST_CASE("default_logger_ints_and_c_array")
{
fs::path const filename{"test_default_logger_ints_and_c_array"};
{
LogManager lm;

quill::Config cfg;
cfg.default_handlers.emplace_back(lm.handler_collection().create_handler<FileHandler>(
filename.string(),
[]()
{
quill::FileHandlerConfig cfg;
cfg.set_open_mode('w');
return cfg;
}(),
FileEventNotifier{}));
lm.configure(cfg);

lm.start_backend_worker(false, std::initializer_list<int32_t>{});

std::thread frontend(
[&lm]()
{
Logger* default_logger = lm.logger_collection().get_logger();

union
{
char no_0[2];
char mid_0[6]{'a', 'b', 'c', 'd', '\0', 'e'};
} v;

// log an array so the log message is pushed to the queue
for (int i = 0; i < 2000; ++i)
{
v.no_0[0] = std::to_string(i).back();
LOG_INFO(default_logger, "Logging int: {}, int: {}, no_0: {}, mid_0: {}", i, i * 10, v.no_0, v.mid_0);
}

// Let all log get flushed to the file
lm.flush();
});

frontend.join();

std::vector<std::string> const file_contents = quill::testing::file_contents(filename);

REQUIRE_EQ(file_contents.size(), 2000);
REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO root Logging int: 0, int: 0, no_0: 0b, mid_0: 0bcd"}));
REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO root Logging int: 1999, int: 19990, no_0: 9b, mid_0: 9bcd"}));

lm.stop_backend_worker();
}
// quill::detail::remove_file(filename);
}

/***/
TEST_CASE("default_logger_ints_and_large_string_dynamic_log_level")
{
Expand Down Expand Up @@ -2273,4 +2331,4 @@ TEST_CASE("default_logger_with_very_large_random_strings")
quill::detail::remove_file(filename);
}

TEST_SUITE_END();
TEST_SUITE_END();
11 changes: 10 additions & 1 deletion quill/test/TypeTraitsCopyableTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,13 @@ TEST_CASE("are_copyable")
static_assert(!are_copyable_v<int, Enum, std::string, NonTrivial>, "_");
}

TEST_SUITE_END();
TEST_CASE("array_size_v")
{
char a[3];
static_assert(array_size_v<decltype(a)> == 3, "_");

using B = char(&)[1];
static_assert(array_size_v<B> == 1, "_");
}

TEST_SUITE_END();

0 comments on commit ebf276a

Please sign in to comment.