programming/C++ sub-category


(Noseglasses) #1

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


(Michael Richters) #2

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.


(Noseglasses) #3

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


(Jonathan Wakely) #4

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.


(Michael Richters) #5

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.