Fixed-capacity, block-allocated container for control-only types (e.g., mutexes, atomics).
More...
#include <jh/synchronous/control_buf.h>
|
|
using | allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<T> |
| | Type of allocator used for element construction.
|
|
| | control_buf ()=default |
| | Constructs an empty control buffer.
|
| | control_buf (const Alloc &alloc) |
| | Constructs an empty control buffer with a custom allocator.
|
| | control_buf (std::size_t n) |
| | Constructs a control buffer with exactly n default-constructed elements.
|
| | control_buf (std::size_t n, const Alloc &alloc) |
| | Constructs a control buffer with n elements using a custom allocator.
|
| | control_buf (const control_buf &other) |
| | Copy-constructs the buffer topology from another instance.
|
| control_buf & | operator= (const control_buf &other) |
| | Copy-assigns the buffer topology from another instance.
|
| | control_buf (control_buf &&other) noexcept |
| | Move-constructs a control buffer from another instance.
|
| control_buf & | operator= (control_buf &&other) noexcept |
| | Move-assigns a control buffer from another buffer.
|
| std::size_t | size () const noexcept |
| | Returns the current number of constructed elements.
|
| std::size_t | capacity () const noexcept |
| | Returns the total number of elements that can be stored without further allocation.
|
| T & | emplace_back () |
| | Appends a default-constructed element to the end of the buffer.
|
| T & | operator[] (std::size_t i) noexcept |
| | Provides unchecked access to the element at index i.
|
| const T & | operator[] (std::size_t i) const noexcept |
| | Provides unchecked, read-only access to the element at index i.
|
| T & | at (std::size_t i) |
| | Returns a reference to the element at index i, with bounds checking.
|
| const T & | at (std::size_t i) const |
| | Returns a const reference to the element at index i, with bounds checking.
|
| void | reserve (std::size_t n) noexcept(noexcept(std::declval< std::vector< block_ptr > & >().reserve(n))) |
| | Reserves space in the internal block index for at least n elements.
|
| void | clear () noexcept(noexcept(std::declval< std::vector< block_ptr > & >().clear())) |
| | Destroys all constructed elements and deallocates all blocks.
|
| void | shrink_to_fit () noexcept |
| | Attempts to reduce the capacity of the internal block index vector to fit its size.
|
| void | resize (std::size_t n) |
| | Resizes the container to contain exactly n elements.
|
Fixed-capacity, block-allocated container for control-only types (e.g., mutexes, atomics).
- Template Parameters
-
| T | Type to store (must be default-constructible and non-reallocable). |
| Alloc | Optional allocator type (defaults to std::allocator<T>). |
Design Constraints
-
T must be default-constructible.
-
T must NOT be copyable or movable.
-
control_buf is not intended for data storage or iteration.
◆ control_buf() [1/6]
template<typename T, typename Alloc = std::allocator<T>>
Constructs an empty control buffer.
No memory is allocated and no elements are constructed. The container starts with zero size and zero blocks.
◆ control_buf() [2/6]
template<typename T, typename Alloc = std::allocator<T>>
Constructs an empty control buffer with a custom allocator.
- Parameters
-
| alloc | Allocator instance used for all future block allocations. |
The allocator is stored internally and copied into each block deleter. No memory allocation occurs at construction time.
◆ control_buf() [3/6]
template<typename T, typename Alloc = std::allocator<T>>
Constructs a control buffer with exactly n default-constructed elements.
- Parameters
-
| n | Number of elements to construct. |
Elements are constructed incrementally using the block-based growth strategy. Each element is default-constructed in-place and never relocated.
◆ control_buf() [4/6]
template<typename T, typename Alloc = std::allocator<T>>
Constructs a control buffer with n elements using a custom allocator.
- Parameters
-
| n | Number of elements to construct. |
| alloc | Allocator used for block allocation and element construction. |
◆ control_buf() [5/6]
template<typename T, typename Alloc = std::allocator<T>>
Copy-constructs the buffer topology from another instance.
- Parameters
-
This is a topological copy:
-
The resulting buffer has the same
size() as other.
-
Elements are default-constructed, not copied.
-
No element-wise copy or move is performed.
This behavior is intentional and required for control-type objects whose state must not be duplicated.
◆ control_buf() [6/6]
template<typename T, typename Alloc = std::allocator<T>>
Move-constructs a control buffer from another instance.
- Parameters
-
| other | Source buffer to move from. |
Transfers ownership of all internal blocks and allocator state from other to the newly constructed buffer.
-
No elements are copied, moved, or relocated.
-
Only block ownership and allocator state are transferred.
-
Block-level address stability is preserved.
- Note
- After the move,
other is left in a valid but unspecified state, consistent with standard C++ container move semantics. The moved-from object is safe to destroy. If reuse is required, the caller must explicitly call clear().
◆ at() [1/2]
template<typename T, typename Alloc = std::allocator<T>>
Returns a reference to the element at index i, with bounds checking.
- Parameters
-
| i | Zero-based index of the element to access. |
- Returns
- Reference to the element at index
i.
- Exceptions
-
| std::out_of_range | If i is greater than or equal to size(). |
This function performs runtime bounds checking before accessing the element. If the index is invalid, an exception is thrown.
-
Semantically equivalent to
std::vector::at().
-
Access is internally delegated to
operator[] after validation.
-
Provides a safer alternative to unchecked access via
operator[].
- Note
- Prefer this method over
operator[] when index validity is uncertain.
◆ at() [2/2]
template<typename T, typename Alloc = std::allocator<T>>
Returns a const reference to the element at index i, with bounds checking.
- Parameters
-
| i | Zero-based index of the element to access. |
- Returns
- Const reference to the element at index
i.
- Exceptions
-
| std::out_of_range | If i is greater than or equal to size(). |
This overload provides read-only, bounds-checked access to an element.
-
Returns the same result as the non-const version but guarantees const access.
-
Delegates to
operator[] after index validation.
- Note
- Use this method when accessing elements in a const context with safety guarantees.
◆ capacity()
template<typename T, typename Alloc = std::allocator<T>>
Returns the total number of elements that can be stored without further allocation.
- Returns
- The maximum number of elements that can be emplaced before triggering a new block allocation.
- Note
- Capacity is managed in blocks of fixed size (
BLOCK_SIZE) and is calculated as: blocks.capacity() * BLOCK_SIZE.
◆ clear()
template<typename T, typename Alloc = std::allocator<T>>
Destroys all constructed elements and deallocates all blocks.
This function releases all memory held by the container and resets its size to zero. It also clears the internal block index vector.
-
All constructed elements are destroyed via allocator-aware destruction.
-
All blocks are properly deallocated via their bound deleters.
-
The internal block pointer vector is also cleared.
- Note
- After calling
clear(), the container is empty and has no allocated blocks.
◆ emplace_back()
template<typename T, typename Alloc = std::allocator<T>>
Appends a default-constructed element to the end of the buffer.
- Returns
- Reference to the newly constructed element.
This method constructs a new element in-place at the logical end of the buffer. If the current block is full, a new block is allocated and all its elements are default-constructed.
-
Elements are constructed using
allocator_traits::construct.
-
New memory is allocated in chunks of
BLOCK_SIZE elements.
-
Each block is allocated only once and never reallocated or moved.
-
Element addresses remain stable for the lifetime of the container.
This function's interface is intentionally consistent with std::vector<T>::emplace_back() and std::deque<T>::emplace_back() (default-construction form), even though this container does not support in-place argument forwarding.
-
This design choice lowers the learning curve for users familiar with STL containers.
-
It enables generic code to use
emplace_back() in the same way across both control and data containers.
-
It allows working with types like
std::mutex and std::atomic, which cannot be stored in std::vector due to relocation or copying constraints, but still benefit from a consistent push-back interface.
- Note
- This function always performs default-construction only. Argument forwarding is not supported.
◆ operator=() [1/2]
template<typename T, typename Alloc = std::allocator<T>>
Copy-assigns the buffer topology from another instance.
- Parameters
-
- Returns
- Reference to this buffer.
Equivalent to:
-
Destroying all existing blocks.
-
Copying the allocator.
-
Default-constructing
other.size() elements.
Element contents are never copied.
◆ operator=() [2/2]
template<typename T, typename Alloc = std::allocator<T>>
Move-assigns a control buffer from another buffer.
- Parameters
-
| other | The source buffer to move from. |
- Returns
- Reference to this buffer after assignment.
Clears current contents and transfers ownership of all internal blocks and allocator state from other to this buffer.
-
All previously allocated memory is destroyed before the move.
-
Element memory is not copied; blocks are moved as raw pointers.
-
The moved-from buffer is left in a valid but unspecified state.
- Note
- As with move construction, reusing the moved-from buffer requires an explicit call to
clear().
◆ operator[]() [1/2]
template<typename T, typename Alloc = std::allocator<T>>
Provides unchecked, read-only access to the element at index i.
- Parameters
-
| i | Zero-based index of the element to access. |
- Returns
- Const reference to the element at the specified index.
This overload offers const access to the same element as the non-const version.
-
No bounds checking is performed.
-
Safe to call as long as
i is within [0, size()).
-
Useful in const contexts and read-only algorithms.
- Note
- Use
at(i) for bounds-checked access.
◆ operator[]() [2/2]
template<typename T, typename Alloc = std::allocator<T>>
Provides unchecked access to the element at index i.
- Parameters
-
| i | Zero-based index of the element to access. |
- Returns
- Reference to the element at the specified index.
This function does not perform bounds checking. It assumes the index is valid (i.e., less than size()).
-
Equivalent in usage to
std::vector::operator[].
-
Allows direct indexed access for integration with generic code.
-
Performs internal block lookup via division and modulus on
BLOCK_SIZE.
- Note
- Use
at(i) if you need bounds checking.
◆ reserve()
template<typename T, typename Alloc = std::allocator<T>>
Reserves space in the internal block index for at least n elements.
- Parameters
-
| n | Desired minimum number of elements the buffer should be able to hold without reallocation. |
This function performs a preallocation on the internal std::vector<block_ptr> that manages block pointers. It does not preallocate the actual element blocks themselves.
-
Internally reserves
ceil(n / BLOCK_SIZE) block pointer slots.
-
Does not allocate or construct any elements or blocks.
-
Useful mostly for reducing index vector reallocations during bulk appends.
-
Cannot avoid per-block allocation due to the semantics of
control_buf.
- Note
- The usefulness of
reserve() is limited because control-type elements (e.g. mutexes) cannot be preconstructed or relocated. This function primarily exists for interface consistency and intent declaration.
◆ resize()
template<typename T, typename Alloc = std::allocator<T>>
Resizes the container to contain exactly n elements.
- Parameters
-
| n | The desired number of elements after resizing. |
This function adjusts the logical size of the container to n.
-
If
n > size(), new elements are added via emplace_back(), which default-constructs new elements in-place.
-
If
n < size(), blocks beyond the necessary range are destroyed and released.
-
If the new size does not align exactly to
BLOCK_SIZE, the tail block may contain partially used space that remains physically allocated but is treated as logically unused.
These remaining elements are not destroyed and retain their previous state. They are considered logically uninitialized, and the next call to emplace_back() will continue from where resize() left off — it does not reinitialize the tail.
- Note
- This behavior is safe and expected for most control types (e.g.,
std::atomic), where default-construction does not guarantee value initialization. However, if you rely on a default-constructed value having a known state (e.g., user-defined types), you must ensure to explicitly reset such objects after resize().
- Warning
- Do not assume that elements beyond the new logical size are reset or zeroed. Treat them as undefined and reinitialize as needed.
◆ shrink_to_fit()
template<typename T, typename Alloc = std::allocator<T>>
Attempts to reduce the capacity of the internal block index vector to fit its size.
This function operates only on the internal std::vector<block_ptr> that stores pointers to element blocks. It does not modify or deallocate any element storage.
-
Does not affect the actual element blocks or constructed elements.
-
Has no observable effect on memory usage unless the vector itself has excess capacity.
-
Provided for interface symmetry with standard containers like
std::vector.
- Note
- This function behaves similarly to
std::vector::shrink_to_fit(), which in C++20 and beyond also has no mandated effect.
◆ size()
template<typename T, typename Alloc = std::allocator<T>>
Returns the current number of constructed elements.
- Returns
- Number of elements currently held in the buffer.
- Note
- This reflects the logical size (i.e., how many elements were constructed via emplace or resize), not the total memory capacity.
The documentation for this class was generated from the following file: