Skip to content

Commit

Permalink
Support logging arithmetic C style arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
odygrd authored Jul 23, 2024
1 parent ffac1d0 commit 31723c2
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- [v5.2.0](#v520)
- [v5.1.0](#v510)
- [v5.0.0](#v500)
- [v4.5.0](#v450)
Expand Down Expand Up @@ -68,6 +69,17 @@
- [v1.1.0](#v110)
- [v1.0.0](#v100)

## v5.2.0

- Added support for passing arithmetic or enum c style arrays when `std/Array.h` is included. For example

```c++
#include "quill/std/Array.h"

int a[6] = {123, 456};
LOG_INFO(logger, "a {}", a);
```
## v5.1.0
- Fix unit tests on FreeBSD ([#496](https://github.com/odygrd/quill/issues/496))
Expand Down
48 changes: 48 additions & 0 deletions quill/include/quill/std/Array.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,54 @@

namespace quill::detail
{
/** Specialization for arrays of arithmetic types and enums **/
template <typename T>
struct ArgSizeCalculator<
T, std::enable_if_t<std::conjunction_v<std::is_array<T>, std::disjunction<std::is_arithmetic<remove_cvref_t<std::remove_extent_t<T>>>, std::is_enum<remove_cvref_t<std::remove_extent_t<T>>>>>>>
{
static size_t calculate(std::vector<size_t>&, T const&) noexcept { return sizeof(T); }
};

/***/
template <typename T>
struct Encoder<
T, std::enable_if_t<std::conjunction_v<std::is_array<T>, std::disjunction<std::is_arithmetic<remove_cvref_t<std::remove_extent_t<T>>>, std::is_enum<remove_cvref_t<std::remove_extent_t<T>>>>>>>
{
static void encode(std::byte*& buffer, std::vector<size_t> const&, uint32_t&, T const& arg) noexcept
{
std::memcpy(buffer, &arg, sizeof(T));
buffer += sizeof(T);
}
};

/***/
template <typename T>
struct Decoder<
T, std::enable_if_t<std::conjunction_v<std::is_array<T>, std::disjunction<std::is_arithmetic<remove_cvref_t<std::remove_extent_t<T>>>, std::is_enum<remove_cvref_t<std::remove_extent_t<T>>>>>>>
{
static void decode(std::byte*& buffer, DynamicFormatArgStore* args_store)
{
using TElem = remove_cvref_t<std::remove_extent_t<T>>;
static constexpr size_t N = std::extent_v<T>;
using array_t = std::array<TElem, N>;

array_t arg;

for (size_t i = 0; i < N; ++i)
{
TElem elem;
std::memcpy(&elem, buffer, sizeof(TElem));
arg[i] = elem;
buffer += sizeof(TElem);
}

if (args_store)
{
args_store->push_back(arg);
}
}
};

/***/
template <typename T, size_t N>
struct ArgSizeCalculator<std::array<T, N>>
Expand Down
167 changes: 167 additions & 0 deletions quill/test/integration_tests/ArithmeticArrayTypesLoggingTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include "doctest/doctest.h"

#include "misc/TestUtilities.h"
#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/bundled/fmt/ostream.h"
#include "quill/sinks/FileSink.h"
#include "quill/std/Array.h"

#include <cstdio>
#include <string>
#include <string_view>
#include <vector>

using namespace quill;

enum TestEnum : int
{
Test1 = 1,
Test2 = 2,
Test3 = 3
};

std::ostream& operator<<(std::ostream& os, TestEnum const& test_enum)
{
switch (test_enum)
{
case TestEnum::Test1:
os << "Test1";
break;
case TestEnum::Test2:
os << "Test2";
break;
case TestEnum::Test3:
os << "Test3";
break;
default:
os << "Unknown";
break;
}
return os;
}

template <>
struct fmtquill::formatter<TestEnum> : fmtquill::ostream_formatter
{
};

enum class TestEnumClass : uint64_t
{
Test4 = 4,
Test5 = 5,
Test6 = 6
};

std::ostream& operator<<(std::ostream& os, const TestEnumClass& test_enum_class)
{
switch (test_enum_class)
{
case TestEnumClass::Test4:
os << "Test4";
break;
case TestEnumClass::Test5:
os << "Test5";
break;
case TestEnumClass::Test6:
os << "Test6";
break;
default:
os << "Unknown";
break;
}
return os;
}

template <>
struct fmtquill::formatter<TestEnumClass> : fmtquill::ostream_formatter
{
};

/***/
TEST_CASE("arithmetic_array_types_logging")
{
static constexpr char const* filename = "arithmetic_array_types_logging.log";
static std::string const logger_name = "logger";

// Start the logging backend thread
Backend::start();

Frontend::preallocate();

// Set writing logging to a file
auto file_sink = Frontend::create_or_get_sink<FileSink>(
filename,
[]()
{
FileSinkConfig cfg;
cfg.set_open_mode('w');
return cfg;
}(),
FileEventNotifier{});

Logger* logger = Frontend::create_or_get_logger(logger_name, std::move(file_sink));

{
bool a[2] = {true, false};
LOG_INFO(logger, "a {}", a);

int b[6] = {123, 456, 789, 321, 654, 987};
LOG_INFO(logger, "b {}", b);

double const c[2] = {123.321, 0};
LOG_INFO(logger, "c {}", c);

auto& d = b;
LOG_INFO(logger, "d {}", d);

auto& e = c;
LOG_INFO(logger, "e {}", e);

std::string y{"test"};
int x = 111;
LOG_INFO(logger, "a b c y d e x {} {} {} {} {} {} {}", a, b, c, y, d, e, x);

TestEnum t{TestEnum::Test2};
LOG_INFO(logger, "t {}", t);

TestEnumClass tc{TestEnumClass::Test5};
LOG_INFO(logger, "tc {}", tc);
}

logger->flush_log();
Frontend::remove_logger(logger);

// Wait until the backend thread stops for test stability
Backend::stop();

// Read file and check
std::vector<std::string> const file_contents = quill::testing::file_contents(filename);

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " a [true, false]"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " b [123, 456, 789, 321, 654, 987]"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " c [123.321, 0]"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " d [123, 456, 789, 321, 654, 987]"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " e [123.321, 0]"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " a b c y d e x [true, false] [123, 456, 789, 321, 654, 987] [123.321, 0] test [123, 456, 789, 321, 654, 987] [123.321, 0] 111"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " t Test2"}));

REQUIRE(quill::testing::file_contains(
file_contents, std::string{"LOG_INFO " + logger_name + " tc Test5"}));

testing::remove_file(filename);
}
1 change: 1 addition & 0 deletions quill/test/integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ endfunction()

include(${PROJECT_SOURCE_DIR}/cmake/doctest.cmake)

quill_add_test(TEST_ArithmeticArrayTypesLogging ArithmeticArrayTypesLoggingTest.cpp)
quill_add_test(TEST_ArithmeticTypesLogging ArithmeticTypesLoggingTest.cpp)
quill_add_test(TEST_BackendExceptionNotifier BackendExceptionNotifierTest.cpp)
quill_add_test(TEST_BackendLongSleepAndNotify BackendLongSleepAndNotifyTest.cpp)
Expand Down

0 comments on commit 31723c2

Please sign in to comment.