programming/C++ sub-category

What about having a programming/C++ subcategory? There are sometimes questions that are rather about C++ than about plugins or the core.

2 Likes

Here’s a question I sent to @noseglasses directly because it didn’t feel appropriate for any existing category. If we had a Programming/C++ category, I’m sure I would have posted it there instead:


If I have a C++ class with a private member variable that’s an array:

class Foo {
private:
  byte foo_[500];
}

…and I want to let other classes access (read/write) that array, what’s the best practice for writing the accessor methods? At least with the limited resources on Arduino, we don’t want to return a copy of the whole array, so do you provide accessor methods that only access a single element at a time?

I’m inclined to make it public for the sake of clarity and simplicity of the code, but I could probably be convinced to use an indexed accessor like:

byte getFoo(byte index) {
  return foo_[index];
}
void setFoo(byte index, byte value) {
  foo_[index] = value;
}

I’m also wondering if there’s a less obvious pattern that is regarded as better than either of these options. Advice is appreciated.

Indexed accessors are very useful as they allow to catch array bound violations by using assertions (make sure that production code is always compiled with -DNDEBUG to suppress assertions).

#include <cassert>

class Foo {
public:
  constexpr int kSize = 500; // for compile time constants, data type size does not matter

  byte getFoo(byte index) const { // always respect const correctness for getters
    assert(index < kSize);
    return foo_[index];
  }
  void setFoo(byte index, byte value) {
    assert(index < kSize);
    foo_[index] = value;
  }
private:
  byte foo_[kSize];
}

When foo_ is the only member, it might be worth considering using overloaded brace operators.

#include <cassert>

class Foo {
public:
  constexpr int kSize = 500; // for compile time constants, data type size does not matter

  byte &operator[](byte index) { // for mutable access
    assert(index < kSize);
    return foo_[index];
  }
  byte operator[](byte index) const { // for const access
    assert(index < kSize);
    return foo_[index];
  }
private:
  byte foo_[kSize];
}

int main() {
   Foo aFoo;
   aFoo[10] = 128; // aFoo[10] returns a non-const reference to a member that can be assigned a value

  const Foo &aConstFoo = aFoo;
  byte value = aConstFoo[10]; // aConstFoo[10] returns an entry of Foo.foo_ by value
  aConstFoo[10] = 11; // will cause a warning "...assignment to temporary..." as we are trying to assign to a copy of the returned array entry
}

See also https://isocpp.org/wiki/faq/const-correctness and https://stackoverflow.com/questions/19237411/const-and-non-const-operator-overloading

You can’t even if you wanted to. In C++ (and C) you can’t pass arrays by value as functions arguments or return types, they decay to pointers.

As suggested, define operator[] overloads to access the array using array-like syntax, not getters and setters.

By “return a copy of the whole array”, I meant copying the class Foo object, which would result in a copy of the member array encapsulated therein.