7
7
#include < new>
8
8
#include < type_traits>
9
9
10
- // / std::is_implicit_lifetime not implemented in g++-12
11
- // / Almost correct. See [P2674R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2674r0.pdf).
12
- template <typename T>
13
- struct is_implicit_lifetime : std::disjunction<
14
- std::is_scalar<T>,
15
- std::is_array<T>,
16
- std::is_aggregate<T>,
17
- std::conjunction<
18
- std::is_trivially_destructible<T>,
19
- std::disjunction<
20
- std::is_trivially_default_constructible<T>,
21
- std::is_trivially_copy_constructible<T>,
22
- std::is_trivially_move_constructible<T>>>> {};
23
- template <typename T>
24
- inline constexpr bool is_implicit_lifetime_v = is_implicit_lifetime<T>::value;
25
-
26
-
27
10
// / A trait used to optimize the number of bytes copied. Specialize this
28
11
// / on the type used to parameterize the Fifo5 to implement the
29
12
// / optimization. The general template returns `sizeof(T)`.
@@ -35,9 +18,9 @@ struct ValueSizeTraits
35
18
};
36
19
37
20
38
- // / Require implicit lifetime , add ValueSizeTraits, pusher and popper to Fifo4
21
+ // / Require trivial , add ValueSizeTraits, pusher and popper to Fifo4
39
22
template <typename T, typename Alloc = std::allocator<T>>
40
- requires is_implicit_lifetime_v <T>
23
+ requires std::is_trivial_v <T>
41
24
class Fifo5 : private Alloc
42
25
{
43
26
public:
@@ -47,19 +30,13 @@ class Fifo5 : private Alloc
47
30
48
31
explicit Fifo5 (size_type capacity, Alloc const & alloc = Alloc{})
49
32
: Alloc{alloc}
50
- , capacity_{capacity}
51
-
52
- // allocate allocates n * sizeof(T) bytes of properly aligned but uninitialized
53
- // storage. Then it creates an array of type T[n] in the storage and starts its
54
- // lifetime, but ***does not start lifetime of any of its elements***.
55
- // Nowhere is "an array of type unsigned char or std::byte"
56
- // mentioned in the description of std::allocator<T>::allocate.
57
- , ring_{allocator_traits::allocate (*this , capacity)}
58
- // , ring_{std::start_lifetime_as_array(allocator_traits::allocate(*this, capacity), capacity)}
59
- {}
33
+ , mask_{capacity - 1 }
34
+ , ring_{allocator_traits::allocate (*this , capacity)} {
35
+ assert ((capacity & mask_) == 0 );
36
+ }
60
37
61
38
~Fifo5 () {
62
- allocator_traits::deallocate (*this , ring_, capacity_ );
39
+ allocator_traits::deallocate (*this , ring_, capacity () );
63
40
}
64
41
65
42
@@ -79,7 +56,7 @@ class Fifo5 : private Alloc
79
56
auto full () const noexcept { return size () == capacity (); }
80
57
81
58
// / Returns the number of elements that can be held in the fifo
82
- auto capacity () const noexcept { return capacity_ ; }
59
+ auto capacity () const noexcept { return mask_ + 1 ; }
83
60
84
61
85
62
// / An RAII proxy object returned by push(). Allows the caller to
@@ -120,35 +97,23 @@ class Fifo5 : private Alloc
120
97
// / Return whether or not the pusher_t is active.
121
98
explicit operator bool () const noexcept { return fifo_; }
122
99
123
- // / In-place construct `value_type` with @a args through std::forward.
124
- template < typename ... Args >
125
- void emplace (Args &&... args) noexcept ;
126
-
127
100
// / @name Direct access to the fifo's ring
128
101
// /@{
102
+ value_type* get () noexcept { return fifo_->element (cursor_); }
103
+ value_type const * get () const noexcept { return fifo_->element (cursor_); }
129
104
130
- // QUESTION
131
- // These get() operations return a reference to one of the
132
- // elements in the T[n] array created by the allocate call
133
- // above. But the T's lifetime has not be started. Do I need to
134
- // call std::start_lifetime_as<T>(fifo->element(cursor_))?
135
- // If so, is it OK if get() happens to be called several times
136
- // on the same storage?
137
- auto & get () noexcept { return *fifo_->element (cursor_); }
138
- auto const & get () const noexcept { return *fifo_->element (cursor_); }
139
-
140
- value_type& operator *() noexcept { return get (); }
141
- value_type const & operator *() const noexcept { return get (); }
142
-
143
- value_type* operator ->() noexcept { return &get (); }
144
- value_type const * operator ->() const noexcept { return &get (); }
105
+ value_type& operator *() noexcept { return *get (); }
106
+ value_type const & operator *() const noexcept { return *get (); }
107
+
108
+ value_type* operator ->() noexcept { return get (); }
109
+ value_type const * operator ->() const noexcept { return get (); }
145
110
// /@}
146
111
147
112
// / Copy-assign a `value_type` to the pusher. Prefer to use this
148
113
// / form rather than assigning directly to a value_type&. It takes
149
114
// / advantage of ValueSizeTraits.
150
115
pusher_t & operator =(value_type const & value) noexcept {
151
- std::memcpy (& get (), std::addressof (value), ValueSizeTraits<value_type>::size (value));
116
+ std::memcpy (get (), std::addressof (value), ValueSizeTraits<value_type>::size (value));
152
117
return *this ;
153
118
}
154
119
@@ -222,20 +187,14 @@ class Fifo5 : private Alloc
222
187
223
188
// / @name Direct access to the fifo's ring
224
189
// /@{
190
+ value_type* get () noexcept { return fifo_->element (cursor_); }
191
+ value_type const * get () const noexcept { return fifo_->element (cursor_); }
225
192
226
- // QUESTION
227
- // If std::start_lifetime_as<T> must be called in the pusher_t
228
- // get() calls must it also be applied here? Or, are the
229
- // pusher_t calls to it and the memcpy sufficient to have an
230
- // actual object in the referenced t[] element?
231
- auto & get () noexcept { return *fifo_->element (cursor_); }
232
- auto const & get () const noexcept { return *fifo_->element (cursor_); }
233
-
234
- value_type& operator *() noexcept { return get (); }
235
- value_type const & operator *() const noexcept { return get (); }
193
+ value_type& operator *() noexcept { return *get (); }
194
+ value_type const & operator *() const noexcept { return *get (); }
236
195
237
- value_type* operator ->() noexcept { return & get (); }
238
- value_type const * operator ->() const noexcept { return & get (); }
196
+ value_type* operator ->() noexcept { return get (); }
197
+ value_type const * operator ->() const noexcept { return get (); }
239
198
// /@}
240
199
241
200
private:
@@ -269,17 +228,17 @@ class Fifo5 : private Alloc
269
228
private:
270
229
auto full (size_type pushCursor, size_type popCursor) const noexcept {
271
230
assert (popCursor <= pushCursor);
272
- return (pushCursor - popCursor) == capacity_ ;
231
+ return (pushCursor - popCursor) == capacity () ;
273
232
}
274
233
static auto empty (size_type pushCursor, size_type popCursor) noexcept {
275
234
return pushCursor == popCursor;
276
235
}
277
236
278
- auto * element (size_type cursor) noexcept { return &ring_[cursor % capacity_ ]; }
279
- auto const * element (size_type cursor) const noexcept { return &ring_[cursor % capacity_ ]; }
237
+ auto * element (size_type cursor) noexcept { return &ring_[cursor & mask_ ]; }
238
+ auto const * element (size_type cursor) const noexcept { return &ring_[cursor & mask_ ]; }
280
239
281
240
private:
282
- size_type capacity_ ;
241
+ size_type mask_ ;
283
242
T* ring_;
284
243
285
244
using CursorType = std::atomic<size_type>;
0 commit comments