0

I'm looking for an easy workaround for two-dimensional arrays of dynamic size as parameters that literally students from the 5th grade can easily use. I'm currently writing a library for a micro controller workshop and want it to be as easy as possible to use without having to explain pointers, templates and such simply because there's not the time to do that.

I have the following class, called LEDBitmap, which has a maximum width of 8 due to constraints in another library and represents a bitmap of ones and zeros for an LED matrix:

The header file:

#ifndef LEDBITMAP_H
#define LEDBITMAP_H

#include <Arduino.h>

class LEDBitmap
{

  public:
    LEDBitmap(uint8_t width, uint8_t height, uint8_t data[][8]);
    LEDBitmap(uint8_t width, uint8_t height);
    virtual ~LEDBitmap() {
      delete [] _data;
    };

  public: //methods
    uint8_t* getBitmap();
    uint8_t getPixelValue(uint8_t x, uint8_t y) {
      return _data[y][x];
    };

    void setPixelValue(uint8_t x, uint8_t y, uint8_t value) {
      _data[y][x] = value;
    };

    uint8_t getHeight() {
      return _height;
    };

    uint8_t getWidth() {
      return _width;
    };

    uint8_t getSize() {
      return _width * _height;
    };

    void clear();

  private: //members
    uint8_t _width;
    uint8_t _height;
    uint8_t _data[][8];


};

#endif

And the cpp file:

#include "LEDBitmap.h"

LEDBitmap::LEDBitmap(uint8_t width, uint8_t height) {
  _width = width;
  _height = height;
  _data = new uint8_t[width][height];
  clear();
}

LEDBitmap::LEDBitmap(uint8_t width, uint8_t height, uint8_t data[][8]) {
  _width = width;
  _height = height;
  _data = data;
}

uint8_t* LEDBitmap::getBitmap() {
  uint8_t result[_height];
  for (uint8_t h = 0; h < _height; h++) {
    uint8_t binary = 0;
    for (uint8_t w = 0; w < _width; w++) {
      binary |= _data[h][w] << (_width - w+1);
    }
    result[h] = binary;
  }
  return result;
}

void LEDBitmap::clear() {
  for (uint8_t h = 0; h < _height; h++) {
    for (uint8_t w = 0; w < _width; w++) {
      _data[h][w] = 0;
    }
  }
}

My problem is the first constructor where the parameter uint8_t data[][8] is supposed to be an array of arbitrary height and a maximum width of 8 but possibly smaller.

I've tried to use templates but realized that this would result in bigger problems in other parts of the code. If I were to use templates, then they could only be used in the constructor without having to do something like LEDBitmap<5, 5> image = ... but rather just using LEDBitmap image = LEDBitmap<5, 5>(...)

I am unable to use vectors since they are not part of the Arduino IDE, which is written in C. I have my library in C++ because I am using some other libraries which are in C++ and originally wanted to use a different platform but due to some issues with that I had to switch to Arduino recently.

The error I'm currently getting is: no known conversion for argument 3 from 'uint8_t [3][3] {aka unsigned char [3][3]}' to 'uint8_t (*)[8] {aka unsigned char (*)[8]}'

Thanks in advance for your tips and help.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Lithimlin
  • 542
  • 1
  • 6
  • 24
  • 1
    `std::vector>` should be the default solution for this kind of problem. There should be no need to change any other code if `LEDBitmap` is properly encapsulated. – François Andrieux May 31 '18 at 14:26
  • 1
    _"I also want to try to stay away from vectors for the sake of simplicity..."_ this is the problem. `std::vector` should be the container of 1st choice. You should be using `std::vector>` .Or teach `C` not `C++` – Richard Critten May 31 '18 at 14:27
  • Yes, should be, but as mentioned above, I want to try to stay away from those for the sake of simplicity since it is much easier for a 5th grader to use arrays – Lithimlin May 31 '18 at 14:28
  • C-arrays have all sorts of issues, decay to pointer; need to use `new` for dynamic size; no idea of their own size. – Richard Critten May 31 '18 at 14:29
  • 1
    Looks like this is for Arduino, and afaik, it does not support `vector` (or the rest of the std lib) oob. – 001 May 31 '18 at 14:30
  • They are going to be programming inside the Arduino IDE where I won't have access to vectors since that is purely C. I have my library in C++ now since I'm using some other libraries which are also in C++ and I planned on using a different platform at first but needed to switch due to some issues with it. – Lithimlin May 31 '18 at 14:30
  • I would think a home rolled matrix type class is what you need. The students don't need to understand how it works inside, they just need to learn how to use it. The you can get away with using a single dimension array on the inside and using the `operator()` for multi-dimension access. See [this](https://stackoverflow.com/questions/2151084/map-a-2d-array-onto-a-1d-array) on how that would map – NathanOliver May 31 '18 at 14:35
  • Looks like you would want a constructor for each possible input width (1 through 8). That could be done with a template; I just don't know how well that would interface with the IDE. I would suggest `std::array`, but I guess that is as much out as vectors. – JaMiT May 31 '18 at 14:38
  • @JaMiT yes, unfortunately they are also out. Pretty much anything from `std::` – Lithimlin May 31 '18 at 14:40
  • What about the first part of my comment? Could something like `template LEDBitmap(uint8_t height, uint8_t data[][WIDTH])` be made to work? – JaMiT May 31 '18 at 14:42
  • @JaMiT as mentioned above, I've tried using a template but ran into some issues such as errors saying `LEDBitmap is not a template` and other parts of my code not working anymore and also needing to be made more complicated. – Lithimlin May 31 '18 at 14:46
  • While you can't use std::vector, I'd be tempted to write a simple one for this task ... – UKMonkey May 31 '18 at 15:01
  • @UKMonkey I'd rather not use too many things at once since it would just make things more complicated – Lithimlin May 31 '18 at 15:03
  • @Lithimlin "Easy to use" and "complicated implementation the kids don't need to worry about" aren't mutually exclusive. Just provide a getter and setter that are easy to use; and always ensure that you grow as required in the setter. – UKMonkey May 31 '18 at 15:06
  • @Lithimlin Actually, you said that templates could only be used in the constructor, which is what I suggested. No matter - if it cannot be used, it cannot be used. Still, there are only 8 cases you care about, so maybe just write out 8 constructors (without templates)? One constructor taking `uint8_t data[][1]`, one taking `uint8_t data[][2]`, etc.? – JaMiT May 31 '18 at 15:15
  • @JaMiT Hmm, that might work, even though it's all but nice. – Lithimlin May 31 '18 at 15:16

1 Answers1

1

C++ does not like variable length arrays (never defined in standard even if supported in some implementations) nor raw 2D arrays. If you do not require true [] operator support, my advice is to use accessor functions:

class Array2D {
    size_t width, height;
    uint8_t *data;

public:
    Array2D(size_t width, size_t height): width(width), height(height() {
        data = new uint8_t[width * height];
    }
    ~Arrayt2D() {
        delete[] data;
    }
    // copy/move ctor-assignment operator omitted for brievety but REQUIRED (law of three/five)

    uint8_t getVal(size_t i, size_t j) const {
        return data[i + j * width];
    }
    uint8_t setVal(uint8_t val, size_t i, size_t j) {
        data[i + j * width] = val;
    }
    /* alternatively to use arr.val(i, j) = value :
    uint8_t& val(size_t i, size_t j) {
        return data[i + j * width];
    }
    */
    // other accessors (height, width) omitted but probably useful...
};

Rather C-ish, but as you cannot use vectors and prefer to avoid templates, it could be a basic skeleton...

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252