How to define Memory in Class Constructor

I understand the potential risks of using dynamic memory allocation so I try to stick to static memory or local memory wherever possible.
My question was with classes. Sometimes I need to declare the size of an array inside a class using the constructor, like this:

class foo {
  public:
    foo(uint8_t ArrSize) {
      //it does not have to be static memory, Just local to the class. 
     //This is just the best example I could think of to express my problem/question
      static byte Array[ArrSize];  // <<< Error, Needs Constant Value
      _ArrPoint = Array;
    }
  private:
    byte *_ArrPoint;
};

void setup() {
  foo X(5);
}

void loop() {

}

But this does not work

Up until now I would solve the issue like this:

#define foo_ArrSize 5

class foo {
  public:
    foo() {}
  private:
    byte Array[foo_ArrSize];
};

void setup() {
  foo X;
}

void loop() {

}

However, It becomes increasingly difficult, especially when you have all these definitions tucked away inside .h files. It becomes even more tricky when I want two objects of the same class with different array sizes, like this:

`#define foo_ArrSize 5

class foo {
  public:
    foo() {
      Serial.println(sizeof(Array));
    }
  private:
    byte Array[foo_ArrSize];
};

void setup() {
  Serial.begin(115200);
  foo X; //<< this one has array of 5
#undef foo_ArrSize
#define foo_ArrSize 3
  foo Y; //<< this one still has array of 5, not 3
}

void loop() {

}`

The only other way I know to solve this problem is with Malloc, whic I really don't like.

I have been stuck with this problem for a long time and never knew how to ask about it. I'm sorry if this explanation is a little hard to follow, I have been struggling to find the correct way to ask about this problem

I'm wondering if it is even a possibility to solve the problem the way I'm thinking.
Idk, I have a bad feeling Dynamic Memory is the only way to solve the problem because of scope problems. I was hoping maybe there is at least a better way aside from #define all the time
Thank you for all your help all in advance

I would use malloc() with care!!

class foo {
  public:
    foo(uint8_t ArrSize) {
      _ArrPoint = (byte*) malloc(ArrSize);
    }
    ~foo() {
      free (_ArrPoint); 
    }
  private:
    byte *_ArrPoint;
};

and free the memory allocation in the destructor
of course can lead to memory fragmentation which in a microcontroller can run you out of RAM

@horace
I understand that and it will work, in small quantities, But I was just surprised at how much I needed this feature. If this were an option, I would have loved to put in in almost every library I have. Almost all of them have configuration that requires changes in memory space used. Maybe this would be fine in something a little beefier like an ESP32 with RTOS, but not in a Arduino

@ issac25e
I would tend to keep clear of malloc() on small microcontrollers keeping to static arrays
when working on PCs I use the STL vector and list classes - of course you can then end up with major memory fragmentation as vectors reallocate
it is easy to get careless with a operating system which supports virtuaal memory and the PC has 64Gbytes of real RAM

Or better, not at all.

If you're programming for an ARM or ESP-based "Arduino", use one of the STL containers like std:vector.

I would argue that it's rarely if ever needed. Why can't you just use an array that's large enough?

For Example:

#include "Arduino.h"
#include <vector>

using byteVector = std::vector<uint8_t>;

class foo {
public:
	foo(size_t s) : arraySize(s) {
		vect.reserve(arraySize);
	}

private:
	size_t arraySize;
	byteVector vect;
};

@nicolajna

I would argue that it's rarely if ever needed . Why can't you just use an array that's large enough?

It is possible, but not always. Sometimes, you would need an array so large that it is simply impractical. I will bring an example I had from about a year ago:
I had a project that involved DMX lighting. A DMX universe, as you know, has 512 channels. At minimum, that would take a quarter of Arduino's RAM just like that. In my setup(most setups actually), you would never need all 512 channels. You would input exactly how many channels you need and it(the constructor/class) will create exactly the right sized buffer just for those channels. If you have multiple Universes, you need different sizes of buffers for each.

You can't simply go out and give 512 bytes to each universe when you will only be using about 10% of the channels. There is a possiblility you might need all 512 channels in a huge setup, but rarely.

In my opinion you should reserve the amount of memory you need for the worst case scenario. If that's "too much" then maybe consider if there's a way to bring the worst case scenario down.

If you want to support the 512 channels you would need to have that amount of memory free anyway.

In some objects I use, it is the user’s job and responsibility to supply the storage.

Like Fastleds library, the user defines the array and passes a pointer in.

a7

@nicolajna
Agreed, but lets say you have two separate Universes where one will only ever need 20 channels but the other may need 200+ channels (I had a very similar situation to this one). Does that mean that both instances of the DMX class should need an array of 200+ bytes? Because if you set a constant buffer "maximum", you can't change it between constructors. Both universes will have to deal with 200+ bytes whether they need it or not.

I know that setting a maximum is the most simplest way to fix a problem like this and I do it all the time but when you throw in multiple Universes + Ethernet + SD, trust me, every byte counts. I just wanted to know if there is an easier way to fix it.
I am sorry if I offended you in any way. I did't mean to, I am just trying to find a solution to a very frustrating problem(actually more of a question) of mine

Yes, that is actually the next best solution that I know of. Problem I have with that is sometimes I want only the class to be allowed to change those values and not the programmer(user) in the front side of the class. If you include a bunch of classes together each with their own respective array, its get super messy very fast and often times I found it just confused the debugging proccess later on.
If there is no other solution, this will be the one I will have to settle on and embrace

I want to make it very clear that you have in no way offended me. I'm just asking questions and giving my opinion.

Then I would model the data differently. Have a list of channels which holds a reference to the universe that they're part of. Then you can have a fixed size list with channels from different universes. If that makes any sense to you?

Granted I don't know how you do it now and I've never worked with DMX but I have dealt with issues like these before.

I'm aware of this, but I don't believe that malloc really saves any. Due to overhead the total might actually end up larger.

I do this too and we're not the only ones. It's a viable strategy.

Do you mean something like this:


class DMX {
  public:
    DMX(byte* arr, uint16_t channels) {
      _arr = arr;
      _arr_size = channels;
    }
  private:
    byte*_arr;
    uint16_t _arr_size;
};

void setup() {
  byte channelBuffer1[213];
  byte channelBuffer2[20];
  DMX universe_1(channelBuffer1, 213);
  DMX universe_2(channelBuffer2, 20);
}

void loop() {

}

I think its similar to what @alto777 suggested also.

void setup() {
  byte channelBuffer1[213];
  byte channelBuffer2[20];

// calculate buffer pointer here…

  DMX universe_1(channelBuffer1, 213);
  DMX universe_2(channelBuffer2, 20);
}

If you are maening that U1 and U2 may be different at run time, thus in need of dynamic allocation, you can collect the info needed and then pass pointers as offsets into the one true array.

In the above, say we determine U1 is 42 and U2 is 777, you could something like

   cBuffer *U1 = theBuffer;
   cBuffer *U2 = theBuffer + 42;
   cBuffer *U3 = theBuffer + 42 + 777;

and pass those to the constructor.

Only less literal prolly. :expressionless:

a7

No that's not what I meant, but it can work as well. What I meant was rethinking the way you layout your data in memory. I've written a short example. I'm used to writing in C so that's what I've written the example in, but the idea should be easily transferable to C++.

Let me know if I need to clarify anything.

#include <stdio.h>
#include <stdint.h>

struct DmxUniverse {
    uint16_t id; // No idea what goes into this thing but it needs a member to compile
};

struct DmxChannel {
    uint16_t channel;
    struct DmxUniverse *universe;
};

static void doSomethingWithTheChannel(struct DmxChannel *channel) {
    printf(
            "Channel %u is part of universe %u\n",
            channel->channel,
            channel->universe->id
            );
}

// The length of this array is the maximum amount of channels that's supported.
static struct DmxChannel channels[10];

#define MAX_CHANNELS (sizeof(channels) / sizeof(struct DmxChannel))

int main() {

    // First we define the universes.
    struct DmxUniverse firstUniverse = {
            .id = 1
    };

    struct DmxUniverse secondUniverse = {
            .id = 2
    };

    // Then we define the channels. Each channel has a pointer (reference) to the
    // universe which it's part of.
    unsigned int i;
    for (i = 0; i < MAX_CHANNELS; i++) {

        struct DmxChannel newChannel = {
                .channel = i
        };

        if ( i < MAX_CHANNELS / 2) {
            newChannel.universe = &firstUniverse;
        } else {
            newChannel.universe = &secondUniverse;
        }

        channels[i] = newChannel;
    }

    // Do something with the channels
    for (i = 0; i < MAX_CHANNELS; i++) {
        doSomethingWithTheChannel(&channels[i]);
    }

    return 0;
}

The output from this program is:

Channel 0 is part of universe 1
Channel 1 is part of universe 1
Channel 2 is part of universe 1
Channel 3 is part of universe 1
Channel 4 is part of universe 1
Channel 5 is part of universe 2
Channel 6 is part of universe 2
Channel 7 is part of universe 2
Channel 8 is part of universe 2
Channel 9 is part of universe 2

It could perhaps even be simplified to:

struct DmxChannel {
    uint16_t universe;
    uint16_t channel;
}

@nicolajna
I think I get what going at. Roughly speaking, you create one enormous array of channels where each channel can be assigned a single universe, any universe that is at hand. The problem I am worried about though, is that each channel will now require an extra 2 bytes just for the Universe id/pointer. That will easily double the amount of memory required.
I like the idea of dynamic IDs and more flexability, but I think in my case, simpler is better. Even though I hate the idea in scale, #16 might be the simplest solution.
Thanks for the idea though, I never thought about using structs in that way before. It will probably be very useful somewhere.

That is a cool idea, I will have to try that lib out. But im guessing it is just an easier way to implement dynamic memory