Reputation: 556
I started writing my circular buffer library, just for fun. I came across a particular problem:
template < class T >
T CircularBuffer<T>::pop()
it returns the type T, but what about the case when the CB is empty, and the user tries to use pop()? Returning a nullptr makes no sense, because nullptr can't be converted to double for instance. Obviously, the exception can be thrown, but is there a more semantically friendly way to do it?
Edit: By "semantically friendly" I mean this:
When pushing in a full CB, it is only natural to throw an exception. Exception should prevent the program from crashing, because there is not enough memory for the new element. Throwing an exception when popping an empty CB seems off to me, semantically, because program is not about to crash. But if it is the only option available, tell me.
Upvotes: 2
Views: 1165
Reputation: 93264
This is a perfect use case for std::optional
or boost::optional
- they are classes which store a value or an empty state.
You can change your signature to:
template < class T >
std::optional<T> CircularBuffer<T>::pop()
And your contract will be something along the lines of:
If the buffer is empty, std::nullopt
will be returned.
Otherwise, a non-empty std::optional<T>
containing the head element will be returned.
Alternatively, consider taking an a "continuation" function that will only be invoked if the pop was successful. Example:
template < class F >
decltype(auto) CircularBuffer<T>::pop(F&& continuation);
Usage:
some_circular_buffer.pop([](auto popped_item)
{
// I will only be called if the pop was successful.
});
You can extend this concept by also taking an additional continuation for the "pop failure" case.
Upvotes: 1