Use intrusive list in logging, and add defer
This commit is contained in:
@ -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
50
asl/base/defer.hpp
Normal 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
32
asl/base/defer_tests.cpp
Normal 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);
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user