Skip to content

Mini Project 8 — Generic Ring Buffer

Difficulty: Intermediate · Estimated effort: 4–5 hours · Prerequisites: Module 12 — Templates, Module 10 — Copying and Resource Management · Combines: class templates, a non-type parameter, value semantics, an iterator-lite interface

No complete solution is provided. You are writing a container, so the standard library is your reference for the interface — not for the code.


1. Scenario

A data logger on a bench records readings continuously, but the bench only wants the most recent n. The logger's author wrote int-only code with a fixed array, and needs the same buffer for double readings and for LogEntry records — without writing the wrap-around logic three times.


2. Problem statement

Build a RingBuffer<T, N> template that keeps the last N items pushed into it, with a compile-time capacity, correct wrap-around behaviour, and value semantics a reader can predict.


3. User requirements

  • [ ] Push an item; when the buffer is full, the oldest item is overwritten.
  • [ ] Ask how many items are held and what the capacity is.
  • [ ] Read an item by position, oldest first.
  • [ ] Copy the buffer and get an independent one.
  • [ ] Instantiate it for numbers, strings and one of your own types.

4. Functional requirements

  • [ ] template <typename T, std::size_t N> class RingBuffer with static storage.
  • [ ] push(const T&) and push(T&&), with move used when it should be.
  • [ ] size(), capacity(), empty(), full(), clear().
  • [ ] operator[](std::size_t) and at(std::size_t); at reports an out-of-range index.
  • [ ] front() and back() accessors, both const and non-const.
  • [ ] Copy assignment must copy the logical order, not the underlying array order.

5. Non-functional requirements

  • [ ] Capacity is a template parameter; no dynamic allocation for the storage.
  • [ ] RingBuffer<T, 0> must either be refused at compile time with a clear message or behave as documented; silence is not acceptable.
  • [ ] Instantiated for at least three types, including one non-trivial type, before submitting.
  • [ ] Builds warning-free with g++ -std=c++17 -Wall -Wextra -Werror.
  • [ ] A template definition lives in a header; a separate .cpp that is never instantiated in that translation unit is the error this constraint exists to prevent.

6. Suggested features

  • A for_each or range-for-style visit taking a callable, oldest first.
  • begin()/end() returning a simple forward iterator — deliberately hard; do it only if the rest is finished.
  • A to_vector() snapshot, with a note on what it costs.
  • A static_assert giving a friendly message for an unsupported type.

7. OOP concepts involved

Concept Where it appears
Class templates One container, many element types
Non-type template parameter Capacity as part of the type, checked at compile time
Encapsulation Head, count and wrap-around arithmetic are private and consistent
Value semantics Copy and move are defined in terms of logical content
RAII Storage belongs to the object; no manual cleanup
static_assert Diagnostics at compile time instead of behaviour at run time

8. Suggested design considerations

Separate logical position from physical position. operator[](0) means oldest. Keeping that true for every operation is most of the work, and hiding it behind one private helper is most of the design.

Write the copy semantics carefully. A member-wise copy of head, count and the array is usually right if the physical order is preserved — but a copy made mid-wrap-around must still answer operator[](0) correctly. Test it after N + 1 pushes.

Non-trivial types cost you care. With std::string or your own type, overwriting a full slot must assign, not skip destruction. If you reach for placement new or memcpy, stop and ask whether that was necessary — it is not.

Decide what N == 0 means. A zero-capacity buffer is a design hole: either static_assert it away with a readable message, or define every query's answer. Both are acceptable; a crash is not.


9. Minimum viable version

A working RingBuffer<T, N> for int with push, size, capacity, operator[], front, back and clear, tested by pushing 2 * N values and checking the retained ones.


10. Intermediate extension

Add copy and move operations, then instantiate the buffer for std::string and one of your own classes. Verify that a copy made after N + 1 pushes has the same logical order as the original.


11. Advanced extension

Add a visitor taking a callable and use it to compute a maximum and a running average. Then add begin()/end() — oldest to newest, exact — and write down what the iterator had to know that the rest of the class does not, which is the honest cost of the feature.


12. Testing checklist

  • [ ] Fewer than N pushes: size is the number pushed, order is insertion order
  • [ ] Exactly N pushes: full, front is the first pushed
  • [ ] N + 1 pushes: the first pushed is gone, front is the second
  • [ ] 2 * N pushes: contents are the last N, in order
  • [ ] at(N) reports an out-of-range error; operator[] is not required to
  • [ ] A copy after wrapping has identical logical order
  • [ ] Instantiation for std::string compiles and behaves the same way
  • [ ] clear() leaves empty() true and capacity() unchanged

13. Documentation checklist

  • [ ] The header states the invariant: count <= N, and position mapping
  • [ ] Why capacity is a template parameter rather than a constructor argument
  • [ ] The N == 0 rule, whether it is a static_assert or defined behaviour
  • [ ] Which operations are noexcept and why
  • [ ] A short usage example in the header comment

14. Self-evaluation rubric

Criterion Excellent looks like Weight
Correctness Logical order correct before, at and after wrap-around 30%
Template design Clean interface; capacity in the type; no ODR problems 25%
Value semantics Copy and move correct mid-wrap; no leaks or double frees 20%
Diagnostics N == 0 and unsupported uses give readable messages 10%
Encapsulation Mapping from logical to physical index in one place 5%
Documentation Invariant and design decisions written down 10%

Related: Mini projects · Project roadmap · All projects · Module 12 — Templates