Search code examples
c++vectorcircular-bufferdeleted-functions

Add class containing a circular buffer to Vector


I am trying to create a vector filled with class objects, and the class objects contain circular buffers as one of their members. I am running into this error:

In file included from .pio/libdeps/teensy40/Vector/src/Vector.h:95:0,
                 from src/main.cpp:2:
.pio/libdeps/teensy40/Vector/src/Vector/VectorDefinitions.h: In instantiation of 'void Vector<T>::push_back(const T&) [with T = Node]':
src/main.cpp:25:27:   required from here
.pio/libdeps/teensy40/Vector/src/Vector/VectorDefinitions.h:162:22: error: use of deleted function 'Node& Node::operator=(const Node&)'
     values_[size_++] = value;
                      ^
src/main.cpp:5:7: note: 'Node& Node::operator=(const Node&)' is implicitly deleted because the default definition would be ill-formed:
 class Node
       ^
src/main.cpp:5:7: error: use of deleted function 'CircularBuffer<T, S, IT>& CircularBuffer<T, S, IT>::operator=(const CircularBuffer<T, S, IT>&) [with T = long unsigned int; unsigned int S = 100u; IT = unsigned char]'
In file included from src/main.cpp:3:0:
.pio/libdeps/teensy40/CircularBuffer/CircularBuffer.h:64:18: note: declared here
  CircularBuffer& operator=(const CircularBuffer&) = delete;
                  ^
*** [.pio/build/teensy40/src/main.cpp.o] Error 1
======================================== [FAILED] Took 0.99 seconds ========================================

I created a program that is the minimum code I could to replicate the error:

#include <Arduino.h>
#include <Vector.h>
#include <CircularBuffer.h>

class Node
{
  CircularBuffer<uint32_t, 100> RTTSamplesBuffer;
};

// Vector to hold the nodes we currently have saved
Vector<Node> Nodes;
// This stuff is used in setup to make the vector not use dynamic allocation
// This version of the Vector library is special for arduino so it is not
// dynamically allocating the vector to prevent filling the controllers memory
const int NODE_COUNT_MAX = 20;
Node storage_array[NODE_COUNT_MAX];

void setup()
{
  Serial.begin(115200);
  // Point the vector to it's storage location.
  Nodes.setStorage(storage_array);

  while (!Serial)
    ;

  // Create Node I want to add to the vector
  Node testNode;

  // Try and add the Node to the vector.
  Nodes.push_back(testNode);

  while (1)
    ;
}

void loop()
{
  // put your main code here, to run repeatedly:
}

I am using this library for vectors as it is compatible with Arduino and won't fill up the memory, and I am using this library for the circular buffer. Thank you!


Solution

  • Looking at the code of the circular buffer:

        CircularBuffer(const CircularBuffer&) = delete;
        CircularBuffer(CircularBuffer&&) = delete;
        CircularBuffer& operator=(const CircularBuffer&) = delete;
        CircularBuffer& operator=(CircularBuffer&&) = delete;
    

    This means that the circular buffer, once allocated, cannot be moved or copied in any way. It stays where it is, and that will also apply to any type that has one of these as a member.

    Adding a level of indirection so that the CircularBuffers are only ever reffered to, instead of being copied around, will get you out of that jam.

    Roughly like this:

    class Node
    {
      using buffer_type = CircularBuffer<uint32_t, 100>;
    
      buffer_type* RTTSamplesBuffer = nullptr;
    
    public:
      set_buffer(buffer_type* b) {
        RTTSamplesBuffer = b;
      }
    };
    
    const int NODE_COUNT_MAX = 20;
    Node storage_array[NODE_COUNT_MAX];
    Node::buffer_type buffers[NODE_COUNT_MAX];
    
    void setup()
    {
      for(int i=0; i<NODE_COUNT_MAX; ++i) {
        storage_array[i].set_buffer(&buffers[i]);
      }
    
      // ...
    }
    

    Normally this is where there "should" be a lot of hand-wringing about the rule of 0/3/5. However, as long as these are pointers to globally allocated objects, it's not strictly necessary.