Skip to content

Commit 4d1ddd8

Browse files
committed
improve field:
- after transitioning it will be a reference-counted object - type_name is now part of the vtable
1 parent 5bf3b5b commit 4d1ddd8

File tree

2 files changed

+116
-88
lines changed

2 files changed

+116
-88
lines changed

include/rsl/logging/field.hpp

Lines changed: 115 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -2,105 +2,136 @@
22
#include <string>
33
#include <format>
44
#include <type_traits>
5+
#include <utility>
56
#include <vector>
67
#include <meta>
8+
#include <atomic>
79

8-
#include <print>
910

1011
#include <rsl/serialize>
1112
#include <rsl/repr>
1213

1314
#include <kwargs.h>
1415

1516
namespace rsl::logging {
16-
namespace _impl {
17-
template <typename T>
18-
struct Impl {
19-
static std::string to_string(void const* p) {
20-
return std::format("{}", *static_cast<const T*>(p));
21-
}
22-
static std::string to_repr(void const* p) { return rsl::repr(*static_cast<const T*>(p)); }
23-
static std::string to_json(void const* p) { return ""; }
17+
class Field {
18+
template <typename T>
19+
struct Impl {
20+
struct RefCounted {
21+
mutable std::atomic_unsigned_lock_free references;
22+
T data;
23+
std::string name;
24+
25+
RefCounted(T obj, std::string_view name) : references(0), data(std::move(obj)), name(name) {}
26+
~RefCounted() = default;
27+
RefCounted(RefCounted const&) = delete;
28+
RefCounted(RefCounted&&) = delete;
29+
RefCounted& operator=(RefCounted const&) = delete;
30+
RefCounted& operator=(RefCounted&&) = delete;
31+
};
32+
33+
static std::string to_string(Field const* field) { return std::format("{}", *get(field)); }
34+
static std::string to_repr(Field const* field) { return rsl::repr(*get(field)); }
35+
static std::string to_json(Field const* field) { return ""; }
36+
37+
static T* get(Field* field) {
38+
if (field->vtable->destroy != nullptr) {
39+
return &static_cast<RefCounted*>(field->ptr)->data;
40+
} else {
41+
return static_cast<T*>(field->ptr);
42+
}
43+
}
2444

25-
static void* clone(void const* p) { return new T(*static_cast<T const*>(p)); }
26-
static void destroy(void* p) { delete static_cast<T*>(p); }
27-
};
45+
static T const* get(Field const* field) { return get(const_cast<Field*>(field)); }
2846

29-
struct VTable {
30-
std::string (*type_name)(void const*);
31-
std::string (*to_string)(void const*);
32-
std::string (*to_repr)(void const*);
33-
std::string (*to_json)(void const*);
34-
void* (*clone)(void const*);
35-
void (*destroy)(void* p);
36-
};
47+
static Field transition(Field const* field) {
48+
RefCounted* ref = new RefCounted(*static_cast<T const*>(field->ptr), field->name);
49+
return Field(ref, make_vtable<T, true>(), ref->name);
50+
}
3751

38-
template <typename T>
39-
constexpr VTable const* make_vtable() {
40-
static constexpr VTable table{.to_string = &Impl<T>::to_string,
41-
.to_repr = &Impl<T>::to_repr,
42-
.to_json = &Impl<T>::to_json,
43-
.clone = &Impl<T>::clone,
44-
.destroy = &Impl<T>::destroy};
45-
return &table;
46-
}
47-
} // namespace _impl
52+
static Field clone(Field const* field) {
53+
auto const& control_block = *static_cast<RefCounted const*>(field->ptr);
54+
// control_block.references++;
55+
auto val = control_block.references.fetch_add(1);
56+
return Field(field->ptr, field->vtable, field->name);
57+
}
4858

49-
class Field {
50-
void* ptr = nullptr;
51-
_impl::VTable const* vtable = nullptr;
59+
static void destroy(void* p) {
60+
auto* control_block = static_cast<RefCounted*>(p);
61+
auto val = control_block->references.fetch_sub(1);
62+
if (val == 0) {
63+
delete control_block;
64+
}
65+
}
66+
};
67+
68+
struct VTable {
69+
std::string_view type_name;
70+
71+
std::string (*to_string)(Field const*);
72+
std::string (*to_repr)(Field const*);
73+
std::string (*to_json)(Field const*);
74+
Field (*clone)(Field const*);
75+
void (*destroy)(void* p);
76+
77+
bool is_owning() const { return destroy != nullptr; }
78+
};
79+
80+
template <typename T, bool Heap>
81+
constexpr static VTable const* make_vtable() {
82+
static constexpr VTable table{.type_name = rsl::type_name<T>,
83+
.to_string = &Impl<T>::to_string,
84+
.to_repr = &Impl<T>::to_repr,
85+
.to_json = &Impl<T>::to_json,
86+
.clone = Heap ? &Impl<T>::clone : &Impl<T>::transition,
87+
.destroy = Heap ? &Impl<T>::destroy : nullptr};
88+
return &table;
89+
}
5290

53-
// TODO maybe count references in owning mode?
54-
bool owning = false;
91+
void* ptr = nullptr;
92+
VTable const* vtable = nullptr;
5593

5694
void destroy() {
57-
if (owning) {
95+
if (vtable != nullptr && vtable->is_owning()) {
5896
vtable->destroy(ptr);
5997
}
6098
ptr = nullptr;
61-
owning = false;
99+
vtable = nullptr;
62100
}
63101

64-
Field(void* ptr, _impl::VTable const* vtable, bool owning, std::string_view name, std::string_view type_name)
65-
: ptr(ptr)
66-
, vtable(vtable)
67-
, owning(owning)
68-
, name(name)
69-
, type_name(type_name) {}
102+
Field(void* ptr, VTable const* vtable, std::string_view name)
103+
: ptr(ptr)
104+
, vtable(vtable)
105+
, name(name) {}
106+
107+
static Field copy_from(Field const& other) {
108+
if (other.vtable->destroy != nullptr) {
109+
return other.vtable->clone(&other);
110+
} else {
111+
return {other.ptr, other.vtable, other.name};
112+
}
113+
}
70114

71115
public:
72116
std::string_view name;
73-
std::string_view type_name;
74117

75118
Field() = default;
76-
77-
Field(const Field& other)
78-
: ptr(other.owning ? other.vtable->clone(other.ptr) : other.ptr)
79-
, vtable(other.vtable)
80-
, owning(other.owning)
81-
, name(other.name)
82-
, type_name(other.type_name) {}
119+
Field(const Field& other) : Field(copy_from(other)) {}
83120

84121
// move constructor
85122
Field(Field&& other) noexcept
86123
: ptr(other.ptr)
87124
, vtable(other.vtable)
88-
, owning(other.owning)
89-
, name(other.name)
90-
, type_name(other.type_name) {
125+
, name(other.name) {
91126
other.ptr = nullptr;
92-
other.owning = false;
127+
other.vtable = nullptr;
93128
}
94129

95130
// copy assignment
96131
Field& operator=(const Field& other) {
97132
if (this != &other) {
98133
destroy();
99-
ptr = other.owning ? other.vtable->clone(other.ptr) : other.ptr;
100-
vtable = other.vtable;
101-
owning = other.owning;
102-
name = other.name;
103-
type_name = other.type_name;
134+
*this = copy_from(other);
104135
}
105136
return *this;
106137
}
@@ -109,45 +140,40 @@ class Field {
109140
Field& operator=(Field&& other) noexcept {
110141
if (this != &other) {
111142
destroy();
112-
ptr = other.ptr;
113-
vtable = other.vtable;
114-
owning = other.owning;
115-
name = other.name;
116-
type_name = other.type_name;
117-
other.ptr = nullptr;
118-
other.owning = false;
143+
ptr = std::exchange(other.ptr, nullptr);
144+
vtable = std::exchange(other.vtable, nullptr);
145+
name = other.name;
119146
}
120147
return *this;
121148
}
122149

123150
template <typename T>
124151
requires(not std::same_as<T, void>)
125-
Field(std::string_view name, T* value, bool needs_cleanup = false)
126-
: name(name)
127-
, type_name(rsl::type_name<T>)
128-
, ptr(value)
129-
, vtable(_impl::make_vtable<T>())
130-
, owning(needs_cleanup) {}
131-
132-
~Field() noexcept {
133-
destroy();
134-
}
152+
Field(std::string_view name, T* value)
153+
: ptr(value)
154+
, vtable(make_vtable<T, false>())
155+
, name(name) {}
156+
157+
~Field() noexcept { destroy(); }
135158

136159
[[nodiscard]] Field clone() const {
137-
return Field(ptr ? vtable->clone(ptr) : nullptr, vtable, true, name, type_name);
160+
if (vtable == nullptr || ptr == nullptr) {
161+
return {};
162+
}
163+
return vtable->clone(this);
138164
}
139165

140166
template <class T>
141-
friend T* any_cast(Field* other) noexcept {
142-
if (!other || !other->ptr) {
167+
friend T* any_cast(Field* field) noexcept {
168+
if (!field || !field->ptr) {
143169
return nullptr;
144170
}
145171

146172
// checking one function in the table is sufficient to determine equality
147-
if (other->vtable->to_string != &_impl::Impl<T>::to_string) {
173+
if (field->vtable->to_string != &Impl<T>::to_string) {
148174
return nullptr;
149175
}
150-
return static_cast<T*>(other->ptr);
176+
return Impl<T>::get(field);
151177
}
152178

153179
template <typename T, typename U>
@@ -160,16 +186,18 @@ class Field {
160186
}
161187
}
162188

163-
[[nodiscard]] std::string to_string() const { return vtable->to_string(ptr); }
164-
[[nodiscard]] std::string to_json() const { return vtable->to_json(ptr); }
165-
[[nodiscard]] std::string to_repr() const { return vtable->to_repr(ptr); }
189+
[[nodiscard]] std::string_view type_name() const { return vtable->type_name; }
190+
[[nodiscard]] std::string to_string() const { return vtable->to_string(this); }
191+
[[nodiscard]] std::string to_json() const { return vtable->to_json(this); }
192+
[[nodiscard]] std::string to_repr() const { return vtable->to_repr(this); }
166193
};
167194

168195
struct ExtraFields {
196+
//TODO this could be a span that views memory on the stack and transitions to heap if its elts do
169197
std::vector<Field> fields;
170198

171199
ExtraFields() = default;
172-
explicit(false) ExtraFields(std::vector<Field> fields) : fields(fields) {}
200+
explicit(false) ExtraFields(std::vector<Field> fields) : fields(std::move(fields)) {}
173201
template <typename T>
174202
requires is_kwargs<std::remove_cvref_t<T>>
175203
explicit(false) ExtraFields(T&& kwargs) {
@@ -215,4 +243,4 @@ struct ExtraFields {
215243
}
216244
};
217245

218-
} // namespace rsl::_log_impl
246+
} // namespace rsl::logging

src/sinks/systemd.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ std::string format_name(std::string name, ExtraFields const& arguments) {
2727
} else {
2828
first = false;
2929
}
30-
name += std::format("{} {}={}", arg.type_name, arg.name, arg.to_repr());
30+
name += std::format("{} {}={}", arg.type_name(), arg.name, arg.to_repr());
3131
}
3232
name += ")";
3333
}

0 commit comments

Comments
 (0)