-1

I found this implementation of a ring buffer with a mutex on github. I paste the code for you to read:

#include <cstdio>

#include <memory>
#include <mutex>

template <class T>
class circular_buffer {
public:
    explicit circular_buffer(size_t size) :
        buf_(std::unique_ptr<T[]>(new T[size])),
        max_size_(size)
    {

    }

void put(T item)
{
    std::lock_guard<std::mutex> lock(mutex_);

    buf_[head_] = item;

    if(full_)
    {
        tail_ = (tail_ + 1) % max_size_;
    }

    head_ = (head_ + 1) % max_size_;

    full_ = head_ == tail_;
}

T get()
{
    std::lock_guard<std::mutex> lock(mutex_);

    if(empty())
    {
        return T();
    }

    //Read data and advance the tail (we now have a free space)
    auto val = buf_[tail_];
    full_ = false;
    tail_ = (tail_ + 1) % max_size_;

    return val;
}

void reset()
{
    std::lock_guard<std::mutex> lock(mutex_);
    head_ = tail_;
    full_ = false;
}

bool empty() const
{
    //if head and tail are equal, we are empty
    return (!full_ && (head_ == tail_));
}

bool full() const
{
    //If tail is ahead the head by 1, we are full
    return full_;
}

size_t capacity() const
{
    return max_size_;
}

size_t size() const
{
    size_t size = max_size_;

    if(!full_)
    {
        if(head_ >= tail_)
        {
            size = head_ - tail_;
        }
        else
        {
            size = max_size_ + head_ - tail_;
        }
    }

    return size;
}

private:
    std::mutex mutex_;
    std::unique_ptr<T[]> buf_;
    size_t head_ = 0;
    size_t tail_ = 0;
    const size_t max_size_;
    bool full_ = 0;
};

int main(void)
{
circular_buffer<uint32_t> circle(10);
printf("\n === CPP Circular buffer check ===\n");
printf("Size: %zu, Capacity: %zu\n", circle.size(), circle.capacity());

uint32_t x = 1;
printf("Put 1, val: %d\n", x);
circle.put(x);

x = circle.get();
printf("Popped: %d\n", x);

printf("Empty: %d\n", circle.empty());

printf("Adding %zu values\n", circle.capacity() - 1);
for(uint32_t i = 0; i < circle.capacity() - 1; i++)
{
    circle.put(i);
}

circle.reset();

printf("Full: %d\n", circle.full());

printf("Adding %zu values\n", circle.capacity());
for(uint32_t i = 0; i < circle.capacity(); i++)
{
    circle.put(i);
}

printf("Full: %d\n", circle.full());

printf("Reading back values: ");
while(!circle.empty())
{
    printf("%u ", circle.get());
}
printf("\n");

printf("Adding 15 values\n");
for(uint32_t i = 0; i < circle.size() + 5; i++)
{
    circle.put(i);
}

printf("Full: %d\n", circle.full());

printf("Reading back values: ");
while(!circle.empty())
{
    printf("%u ", circle.get());
}
printf("\n");

printf("Empty: %d\n", circle.empty());
printf("Full: %d\n", circle.full());

return 0;
}

At the end of the code chunk you can find a main funcion with an example. There, the queue is initialized with space for 10 elemets as

circular_buffer<uint32_t> circle(10);

As I want to use one of these ring buffers as an object in another class for testing, I have been trying to declare the buffer and then initialize it with a variable indicating the size. This is just an example of what I want to accomplish, as usually done in java:

int size = 20;
circular_buffer<uint16_t> ring;
ring = new circular_buffer<uint16_t>(size);

So far all the things I tried do not compile. Is this possible in C++ with a templated class?

Thanks.

Gabriel
  • 167
  • 1
  • 2
  • 11
  • 2
    Looks like you are trying to lear C++ using trial and error method. This will only lead to pain and suffering. Instead, use structural learning. A good place to start can be [a good C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – SergeyA Jun 25 '21 at 18:31
  • The declaration of ring "circular_buffer ring;" actually initializes the object at the same time. The new operator is to allocate the object on the heap. You need to learn how the constructors and other operators work in C++, https://learn.microsoft.com/en-us/cpp/cpp/constructors-cpp?view=msvc-160. You are probably looking to do "circular_buffer* ring;" instead, which declares a reference to a circular_buffer instead of an actual object. – Richard Bamford Jun 25 '21 at 18:36
  • I see Richard, thanks for the useful link :). Yeah, that makes sense, and then assign the pointer to the value returned by "new ...". Thank you – Gabriel Jun 25 '21 at 18:40

1 Answers1

1

You've declare ring on the stack with automatic storage duration. It's not a pointer:

circular_buffer<uint16_t> ring;

But then you try to construct it dynamically on the heap with new, which expects ring to be a pointer type (e.g. circular_buffer<uint16_t>* ring; )

There is no need for dynamic allocation in your sample , you can just construct it directly:

int size = 20;
circular_buffer<uint16_t> ring{size};
zdan
  • 28,667
  • 7
  • 60
  • 71
  • Thanks zdan. But since, as I said, I need the buffer to be an object of another class, I need the dynamic allocation. With the info that you and Richard Bambord gave, I guess it is a matter of defining `circular_buffer* ring;` and then assigning `ring = new circular_buffer(size);`. If you would like to add this option as an alternative answer, I can accept it later – Gabriel Jun 25 '21 at 18:51
  • @Gabriel or you could have your other class construct the ring buffer and have a method to return a reference or pointer to it (or wrapper methods to insert/remove from it). – Miles Budnek Jun 25 '21 at 19:00
  • Interesting @Miles, thank you :) – Gabriel Jun 25 '21 at 19:20