Example input
[task_description]: Implement a thread-safe, templated circular buffer in C++
[cpp_version]: C++17
[development_environment]: Visual Studio 2019
[project_type]: Library component
[input_specification]: emplate parameter for data type, buffer size as constructor parameter
[output_specification]: Thread-safe push and pop operations
[performance_requirements]: O(1) time complexity for push and pop operations
[memory_constraints]: Fixed-size buffer, no dynamic allocation after initialization
[algorithm_choice]: Circular buffer using std::array
[design_patterns]: RAII for resource management
[error_handling_approach]: Use std::optional for pop operations on empty buffer
[code_style_guide]: Google C++ Style Guide
[documentation_level]: Doxygen-compatible comments for public API
[testing_requirements]: Unit tests using Google Test, including concurrency tests
[optimization_focus]: Lock-free operations where possible
[additional_features]: Iterator support for traversing buffer contents
Example output
I'll guide you through implementing a thread-safe circular buffer following the provided process.
1. Task Analysis and Environment Setup:
- Core task: Implement a thread-safe circular buffer with template support
- Environment: Visual Studio 2019 with C++17 support
- Component type: Reusable library component
2-4. Design and Implementation:
Let's implement the circular buffer header file first:
```cpp
#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H
#include <array>
#include <mutex>
#include <optional>
#include <atomic>
#include <iterator>
#include <type_traits>
template <typename T>
class CircularBuffer {
public:
class Iterator; // Forward declaration
explicit CircularBuffer(size_t capacity)
: buffer_(capacity)
, capacity_(capacity)
, head_(0)
, tail_(0)
, size_(0) {}
/**
* @brief Attempts to push an element into the buffer
* @param item Item to push
* @return true if successful, false if buffer is full
*/
bool push(const T& item) {
std::lock_guard<std::mutex> lock(mutex_);
if (size_ == capacity_) {
return false;
}
buffer_[tail_] = item;
tail_ = (tail_ + 1) % capacity_;
++size_;
return true;
}
/**
* @brief Attempts to pop an element from the buffer
* @return std::optional containing the popped item if successful
*/
std::optional<T> pop() {
std::lock_guard<std::mutex> lock(mutex_);
if (size_ == 0) {
return std::nullopt;
}
T item = buffer_[head_];
head_ = (head_ + 1) % capacity_;
--size_;
return item;
}
/**
* @brief Returns current number of elements in buffer
*/
size_t size() const {
return size_.load(std::memory_order_relaxed);
}
/**
* @brief Returns buffer capacity
*/
size_t capacity() const { return capacity_; }
/**
* @brief Checks if buffer is empty
*/
bool empty() const {
return size() == 0;
}
/**
* @brief Checks if buffer is full
*/
bool full() const {
return size() == capacity_;
}
// Iterator support
Iterator begin() { return Iterator(*this, head_); }
Iterator end() { return Iterator(*this, (head_ + size_) % capacity_); }
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
Iterator(CircularBuffer& buf, size_t pos)
: buffer_(buf), pos_(pos) {}
reference operator*() { return buffer_.buffer_[pos_]; }
pointer operator->() { return &buffer_.buffer_[pos_]; }
Iterator& operator++() {
pos_ = (pos_ + 1) % buffer_.capacity_;
return *this;
}
Iterator operator++(int) {
Iterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const Iterator& other) const {
return pos_ == other.pos_;
}
bool operator!=(const Iterator& other) const {
return !(*this == other);
}
private:
CircularBuffer& buffer_;
size_t pos_;
};
private:
std::array<T, capacity_> buffer_;
const size_t capacity_;
size_t head_;
size_t tail_;
std::atomic<size_t> size_;
mutable std::mutex mutex_;
};
#endif // CIRCULAR_BUFFER_H
```
Would you like me to explain this implementation or proceed with providing unit tests and usage examples?