Use intrusive list in logging, and add defer

This commit is contained in:
2025-02-20 23:47:02 +01:00
parent 409ef997e2
commit aa73023bee
10 changed files with 159 additions and 59 deletions

View File

@ -4,6 +4,7 @@ cc_library(
"annotations.hpp",
"assert.hpp",
"config.hpp",
"defer.hpp",
"float.hpp",
"functional.hpp",
"integers.hpp",
@ -28,6 +29,7 @@ cc_library(
"//asl/types:box",
],
) for name in [
"defer",
"float",
"functional",
"integers",

50
asl/base/defer.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include "asl/base/utility.hpp"
#include "asl/base/functional.hpp"
namespace asl
{
// @Todo Add invokable check
template<typename Callback>
class DeferCallback
{
Callback m_callback;
bool m_moved = false;
public:
template<typename T>
explicit DeferCallback(T&& callback) : m_callback(ASL_FWD(callback))
{
}
ASL_DELETE_COPY(DeferCallback);
DeferCallback(DeferCallback&& other) :
m_callback(ASL_MOVE(other.m_callback)), m_moved(exchange(other.m_moved, true))
{
}
DeferCallback& operator=(DeferCallback&&) = delete;
~DeferCallback()
{
if (!m_moved) { invoke(m_callback); }
}
};
struct DeferFactory
{
// @Todo Add invokable check
template<typename Callback>
DeferCallback<Callback> operator<<(Callback&& callback) const
{
return DeferCallback<Callback>(ASL_FWD(callback));
}
};
} // namespace asl
#define ASL_DEFER auto ASL_CONCAT(_defer_, __COUNTER__) = ::asl::DeferFactory{} <<

32
asl/base/defer_tests.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "asl/base/defer.hpp"
#include "asl/testing/testing.hpp"
ASL_TEST(defer)
{
uint32_t a = 0;
{
ASL_DEFER [&a]() { a |= 1; };
ASL_TEST_EXPECT(a == 0);
{
ASL_DEFER [&a]() { a |= 2; };
ASL_DEFER [&a]() { a |= 4; };
ASL_TEST_EXPECT(a == 0);
}
ASL_TEST_EXPECT(a == 6);
{
ASL_DEFER [&a]() { a |= 8; };
ASL_TEST_EXPECT(a == 6);
}
ASL_TEST_EXPECT(a == 14);
ASL_DEFER [&a]() { a |= 16; };
ASL_TEST_EXPECT(a == 14);
}
ASL_TEST_EXPECT(a == 31);
}

View File

@ -92,4 +92,7 @@ constexpr bool is_pow2(isize_t v)
ASL_DEFAULT_COPY(T) \
ASL_DEFAULT_MOVE(T)
#define ASL_CONCAT2(A, B) A##B
#define ASL_CONCAT(A, B) ASL_CONCAT2(A, B)
} // namespace asl

View File

@ -34,6 +34,11 @@ class IntrusiveList
public:
constexpr IntrusiveList() = default;
explicit IntrusiveList(T* head)
{
push_front(head);
}
ASL_DELETE_COPY(IntrusiveList)
ASL_DEFAULT_MOVE(IntrusiveList)
~IntrusiveList() = default;
@ -121,7 +126,7 @@ public:
{
if (!is_empty())
{
T* node = m_head->prev;
T* node = m_head->m_prev;
detach(node);
return node;
}

View File

@ -194,33 +194,33 @@ ASL_TEST(pop_front)
ASL_TEST_ASSERT(list.is_empty());
}
// ASL_TEST(pop_back)
// {
// IntNode one{1};
// IntNode two{2};
// IntNode three{3};
// asl::IntrusiveList<IntNode> list;
ASL_TEST(pop_back)
{
IntNode one{1};
IntNode two{2};
IntNode three{3};
asl::IntrusiveList<IntNode> list;
// list.push_back(&one);
// list.push_back(&two);
// list.push_back(&three);
list.push_back(&one);
list.push_back(&two);
list.push_back(&three);
// IntNode* n = list.pop_back();
// ASL_TEST_ASSERT(n != nullptr);
// ASL_TEST_ASSERT(!list.is_empty());
// ASL_TEST_EXPECT(n->value == 3);
IntNode* n = list.pop_back();
ASL_TEST_ASSERT(n != nullptr);
ASL_TEST_ASSERT(!list.is_empty());
ASL_TEST_EXPECT(n->value == 3);
// n = list.pop_back();
// ASL_TEST_ASSERT(n != nullptr);
// ASL_TEST_ASSERT(!list.is_empty());
// ASL_TEST_EXPECT(n->value == 2);
n = list.pop_back();
ASL_TEST_ASSERT(n != nullptr);
ASL_TEST_ASSERT(!list.is_empty());
ASL_TEST_EXPECT(n->value == 2);
// n = list.pop_back();
// ASL_TEST_ASSERT(n != nullptr);
// ASL_TEST_ASSERT(list.is_empty());
// ASL_TEST_EXPECT(n->value == 1);
n = list.pop_back();
ASL_TEST_ASSERT(n != nullptr);
ASL_TEST_ASSERT(list.is_empty());
ASL_TEST_EXPECT(n->value == 1);
// n = list.pop_back();
// ASL_TEST_ASSERT(n == nullptr);
// ASL_TEST_ASSERT(list.is_empty());
// }
n = list.pop_back();
ASL_TEST_ASSERT(n == nullptr);
ASL_TEST_ASSERT(list.is_empty());
}

View File

@ -8,7 +8,7 @@ cc_library(
],
deps = [
"//asl/base",
"//asl/types:box",
"//asl/containers:intrusive_list",
"//asl/formatting",
"//asl/io:print",
"//asl/strings:string_builder",

View File

@ -5,7 +5,27 @@
// @Todo Don't use internal get_stdout_writer, make console module
static asl::log::DefaultLogger<asl::Writer*> g_default_logger{asl::print_internals::get_stdout_writer()};
static asl::log::Logger* g_head = &g_default_logger;
// @Todo Protect the loggers list being a mutex
static asl::IntrusiveList<asl::log::Logger> g_loggers(&g_default_logger);
void asl::log::register_logger(Logger* logger)
{
g_loggers.push_front(logger);
}
void asl::log::unregister_logger(Logger* logger)
{
g_loggers.detach(logger);
}
void asl::log::remove_default_logger()
{
if (g_default_logger.m_next != nullptr)
{
g_loggers.detach(&g_default_logger);
}
}
static constexpr asl::string_view kLevelName[] = {
" DEBUG ",
@ -14,12 +34,6 @@ static constexpr asl::string_view kLevelName[] = {
" ERROR ",
};
void asl::log::register_logger(box<Logger> logger_box)
{
auto* logger = leak(ASL_MOVE(logger_box));
logger->m_next = exchange(g_head, logger);
}
void asl::log::DefaultLoggerBase::log_inner(Writer& writer, const message& msg)
{
asl::format(&writer, "[{}] {}:{}: {}\n",
@ -44,9 +58,9 @@ void asl::log::log_inner(
.location = sl,
};
for (auto* it = g_head; it != nullptr; it = it->next_logger())
for (auto& logger: g_loggers)
{
it->log(m);
logger.log(m);
}
}

View File

@ -1,8 +1,8 @@
#pragma once
#include "asl/base/utility.hpp"
#include "asl/types/box.hpp"
#include "asl/formatting/format.hpp"
#include "asl/containers/intrusive_list.hpp"
namespace asl::log
{
@ -22,21 +22,14 @@ struct message
source_location location;
};
// @Todo Write and use an intrusive doubly-linked list
class Logger
class Logger : public intrusive_list_node<Logger>
{
Logger* m_next{};
public:
Logger() = default;
ASL_DEFAULT_COPY_MOVE(Logger);
virtual ~Logger() = default;
virtual void log(const message&) = 0;
friend void register_logger(box<Logger>);
constexpr Logger* next_logger() const { return m_next; }
};
class DefaultLoggerBase : public Logger
@ -59,17 +52,13 @@ public:
}
};
void register_logger(box<Logger>);
void register_logger(Logger*);
void unregister_logger(Logger*);
void remove_default_logger();
// @Todo Add a way to remove loggers (including all)
template<typename T, typename... Args>
requires constructible_from<T, Args&&...> && convertible_from<Logger*, T*>
void register_logger(Args&&... args)
{
register_logger(make_box<T>(ASL_FWD(args)...));
}
void log_inner(level l, string_view fmt, span<const format_internals::type_erased_arg> args, const source_location& sl);
template<formattable... Args>

View File

@ -1,6 +1,7 @@
#include "asl/logging/logging.hpp"
#include "asl/testing/testing.hpp"
#include "asl/strings/string_builder.hpp"
#include "asl/base/defer.hpp"
ASL_TEST(log)
{
@ -9,15 +10,19 @@ ASL_TEST(log)
ASL_LOG_ERROR("Oh no! {}", 42);
}
static asl::StringWriter g_string_writer{};
ASL_TEST(custom_writer)
{
asl::log::register_logger<asl::log::DefaultLogger<asl::StringWriter<>&>>(g_string_writer);
asl::StringWriter string_writer{};
asl::log::DefaultLogger<asl::StringWriter<>&> logger(string_writer);
asl::log::register_logger(&logger);
ASL_DEFER [&logger]() {
asl::log::unregister_logger(&logger);
};
ASL_LOG_INFO("Hello");
auto sv = g_string_writer.as_string_view();
auto sv = string_writer.as_string_view();
ASL_TEST_EXPECT(sv == "[ INFO ] asl/logging/logging_tests.cpp:18: Hello\n");
ASL_TEST_EXPECT(sv == "[ INFO ] asl/logging/logging_tests.cpp:23: Hello\n");
}