Creating Instance of a Class with for loop

Happy New Year Everyone!

I’m currently working on a project where I’m using my own class, which will eventually become a library. I would like the ability to create instances of a class “dynamically” with a for loop and also call the class with the for loop. I was searching last night and was unable to find anything on the internet and was wondering if maybe some of you could help. I have provide some snippets below in order to help you understand what I’m doing. The example is only using four (4) instances, but there could be as much as 16 or more.

Top of Sketch with Global Variables:

#include <SoftwareSerial.h>

const int n = 4; //Number of toggles attached 
const int as = n+1; //The size of the array in order to ignore position [0]
#define toggleDelay 100 //Delay toggle for ~ 1/10 of a second


/* TOGGLE VARIABLES */
const int togglePin[as] = {0, 2, 3, 4, 5}; //Digital Pins the toggles are attached to
int toggleInput[as]; //Value read from the toggle at input
int toggleLast[as]; //Last known toggle state 
int toggleState[as]; //DISPLAY and USEABLE state of the toggle

/* SERIAL VARIABLES */
int serialState[as]; //Toggle state via serial 
boolean serial[as]; //Boolean to see if the state was changed by serial

Arduino Setup Function:

void setup() {

 //DYNAMIC INSTANCE OF CLASS 
 for(int i = 1; i <= n; i++) {

  /* 

  Used to create the class instance based on variable "i", would like to be able to have the instance 
  have a custom name, which includes variable "i". The instance will have to pass the togglePin[i].

 */
}
   
//Start Serial 
Serial.begin(9600);

//START UP FUNCTIONS
for(int i = 0; i <= n; i++) {

/*

The purpose of this is to get last known toggle states from a master over serial connection, 
so this function will update the class global variables to known state. 
I would like to make this dynamic as well like something:

custom[i].startup(); 

*/

}


}

Arduino Loop Function:

void loop() {
 
 for(int i = 1; i <= n; i++) {

/*

This is the same condition as the startup function in the setup function. This is the loop
in the custom class. 

custom[i].Update(); 

*/
 
}

 
 //Delay
 delay(toggleDelay); 

 for(int i = 1; i <= n; i++) {

/*

This were the class updates the sketch variable toggleLast[i] 

toggleLast[i] = custom[i].lastState(); 

*/
 
}

I hope this explains what I’m trying to do.

Thanks in advance for the help,

Chris

I think you may be misusing the word dynamic and it may be throwing me off. Dynamic in this sense means that the instances are created and destroyed in heap memory using malloc. I don't think that's actually what you want to do because you say this:

Used to create the class instance based on variable "i", would like to be able to have the instance 
  have a custom name, which includes variable "i".

You can't think about custom names. You have to understand that once your code compiles and get uploaded to the board all the variable "names" are long gone and replaced with memory addresses. The names are just there for your convenience when you are writing the code. So, no, you can't use i in the name anywhere.

But what it really sounds like you want and what it looks like from the syntax you try to use later:

custom[i].Update();

is just that you want an array of your objects. That's totally doable. You create an array of instances of a class just like you create an array of anything else.

Sorry for using the word dynamic. I didn't know what word to use.

I would like to create a class instance using a for loop and it would be okay for that instance to be stored in array. I have no problem with that as long as the problem will create each instance for me. Then call specific functions like Update(), startUp(), etc. based on the instance that was created.

Chris

If you can do something likeString theStrings[5]; I'm sure you can doYourClass array_of_yourclass[5]; and next in a loop access the methods.

I'm however not a C++ programmer (far more C oriented).

Dynamic names will not work but you can always use a #define if you want to access an element in an array by a sensible name; e.g.

#define BTN_ON 0
#define BTN_LEFT 1
#define BTN_RIGHT 2

void someFunc()
{
  array_of_yourclass[BTN_ON].update();
}

Why do you call your pulse generation toggle?

Do not use delay(toggleDelay); to generate you pulse timing, use millis().

You are wasting a lot of memory memory in your arrays.

You should create your classes once and only use them in loop.

You could have a static queue member that holds all objects you created and iterate over it, even with a static object& operator function, having an access function that takes the number of the object, so you would need no array of objects, or array of object pointers/references.

But with the small snippets you provided I can only guess which variables should be members and what functionality you want to have.

Because it was a rather interesting problem I tried to write an example.

class DynTest {
  public:
    DynTest(byte idVal, byte inPin);
    ~DynTest();
    void init();
    void process(unsigned long refTime);
    void on();
    void off();
    void toggle();
    void pulse(unsigned int forMs);
    void printMe();
    static void initAll();
    static void processAll(unsigned long refTime);
    static int count();
    static void printAll();
    static DynTest* accs(size_t index);
    static DynTest* accsId(byte searchedId);
  private:
    static DynTest* anchor;
    DynTest* next;
    byte id;
    byte pin;
    byte pulsing;
    unsigned long lastTime;
    unsigned int duration;
    static DynTest* enqueue(DynTest* what);
    static DynTest* remove(DynTest* what);
};

DynTest glob1(5, 6);
DynTest glob2(6, 7);

void setup() {
  Serial.begin(250000);
  DynTest* p = new DynTest(1, 2);
  p = new DynTest(2, 3);
  p = new DynTest(3, 4);
  p = new DynTest(4, 5);
  p = p; // to make the compiler happy

  DynTest::initAll();

  DynTest::accs(1)->on();
  DynTest::accs(2)->pulse(1000);
  DynTest::accs(3)->pulse(500);
  DynTest::accs(4)->toggle();

  DynTest::printAll();
  Serial.println(F("end of setup"));
}

void loop() {
  static unsigned long lastTime;
  unsigned long topLoop = millis();
  if (topLoop - lastTime >= 1000) {
    DynTest* mod = DynTest::accs(1 + random(DynTest::count()));
    switch (random(3000) & 15) {
      case 0:
        mod->off();
        Serial.print(F("off   "));
        break;
      case 1:
        mod->on();
        Serial.print(F("on    "));
        break;
      case 2:
        Serial.print(F("flip  "));
        mod->toggle();
        break;
      default:
        Serial.print(F("pulse "));
        mod->pulse(random(5000));
    }
    mod->printMe();
    lastTime = topLoop;
  }
  DynTest::processAll(topLoop);
}


DynTest* DynTest::anchor = NULL;

DynTest* DynTest::accs(size_t index) {
  DynTest* q = anchor;
  for (; q != NULL && index > 1; index--) {
    q = q->next;
  }
  return q;
}

DynTest* DynTest::accsId(byte searchedId) {
  DynTest* q = anchor;
  while (q != NULL && q->id != searchedId) {
    q = q->next;
  }
  return q;
}

int DynTest::count() {
  DynTest* q = anchor;
  int number = 0;
  while (q != NULL) {
    q = q->next;
    number++;
  }
  return number;
}

DynTest* DynTest::enqueue(DynTest * what) {
  DynTest* q = anchor;
  what->next = NULL;
  if (q == NULL) {
    anchor = what;
  } else {
    while (q->next != NULL) {
      q = q->next;
    }
    q->next = what;
  }
  return what;
}

DynTest* DynTest::remove(DynTest* what) {
  if (anchor == what) {
    anchor = what->next;
    what->next = NULL;
  } else {
    DynTest* p = anchor;
    for (; p != NULL; p = p->next) {
      if (p->next == what) {
        p->next = what->next;
        what->next = NULL;
        break;
      }
    }
  }
  return what;
}

void DynTest::printAll() {
  for (byte i = 1; i <= count(); i++) {
    accs(i)->printMe();
  }
}

DynTest::DynTest(byte idVal, byte inPin) : id(idVal), pin(inPin) {
  pulsing = 0;
  enqueue(this);
}

DynTest::~DynTest() {
  off();
  remove(this);
}

void DynTest::on() {
  digitalWrite(pin, HIGH);
  pulsing = 0;
}

void DynTest::off() {
  digitalWrite(pin, LOW);
  pulsing = 0;
}

void DynTest::toggle() {
  digitalWrite(pin, !digitalRead(pin));
  pulsing = 0;
}

void DynTest::pulse(unsigned int forMs) {
  duration = forMs;
  lastTime = millis();
  pulsing = 1;
  digitalWrite(pin, HIGH);
}

void DynTest::init() {
  pinMode(pin, OUTPUT);
}

void DynTest::process(unsigned long refTime) {
  if (pulsing) {
    if (refTime - lastTime >= duration) {
      off();
    }
  }
}

void DynTest::initAll() {
  for (DynTest* p = anchor; p != NULL; p = p->next) {
    p->init();
  }
}
void DynTest::processAll(unsigned long refTime) {
  for (DynTest* p = anchor; p != NULL; p = p->next) {
    p->process(refTime);
  }
}

void DynTest::printMe() {
  Serial.print(F("id = "));
  Serial.print(id);
  Serial.print(F(", pin = "));
  Serial.print(pin);
  Serial.print(F(", state = "));
  Serial.print(digitalRead(pin));
  Serial.print(F(", pulse = "));
  Serial.print(pulsing);
  if (pulsing) {
    Serial.print(F(", duration = "));
    Serial.print(duration);
  }
  Serial.println();
}
id = 5, pin = 6, state = 1, pulse = 0
id = 6, pin = 7, state = 1, pulse = 1, duration = 1000
id = 1, pin = 2, state = 1, pulse = 1, duration = 500
id = 2, pin = 3, state = 1, pulse = 0
id = 3, pin = 4, state = 0, pulse = 0
id = 4, pin = 5, state = 0, pulse = 0
end of setup
on    id = 6, pin = 7, state = 1, pulse = 0
pulse id = 4, pin = 5, state = 1, pulse = 1, duration = 3930
off   id = 1, pin = 2, state = 0, pulse = 0
pulse id = 1, pin = 2, state = 1, pulse = 1, duration = 2709
pulse id = 5, pin = 6, state = 1, pulse = 1, duration = 4492
pulse id = 1, pin = 2, state = 1, pulse = 1, duration = 2503
on    id = 6, pin = 7, state = 1, pulse = 0
pulse id = 3, pin = 4, state = 1, pulse = 1, duration = 4303
pulse id = 6, pin = 7, state = 1, pulse = 1, duration = 2157
pulse id = 3, pin = 4, state = 1, pulse = 1, duration = 3099
off   id = 5, pin = 6, state = 0, pulse = 0
on    id = 6, pin = 7, state = 1, pulse = 0

I did some research earlier and I finally tested. I was able to do what I wanted by using the following:

Toggle* toggle = new Toggle[as];
for(int i = 0; i <= n; i++) {
toggle[i].someFunction(); 
toggle[i].state = toggleState[i];
}

Thanks for everyone for the help.

Chris

Toggle* toggle = new Toggle[as];

Now that IS dynamic allocation.

Typically, I use this pattern:

struct Toggle {
  const byte pin;
  int input; //Value read from the toggle at input
  int last; //Last known toggle state
  int state; //DISPLAY and USEABLE state of the toggle

  Toggle(byte attachPin) : pin(attachPin) {
  }

  void setup() {
    pinMode(pin, INPUT_PULLUP);
  }

  void update() {
    last = state;
    state = digitalRead(pin);
    // whatever other logic goes here
  }
};

Toggle toggle[] = {
  Toggle(2),
  Toggle(3),
  Toggle(4),
  Toggle(5)
};

const int NTOGGLES = sizeof(toggle) / sizeof(toggle[0]); 

void setup() {
  for(int i = 0; i< NTOGGLES; i++) {
    toggle[i].setup();
  }
}


void loop() {
  for(int i = 0; i< NTOGGLES; i++) {
    toggle[i].update();
  }
}

Note that the Toggle constructor does not attempt to setup the pin - there’s a separate setup method to do that. This is because constructors are invoked before any of the hardware stuff that happens on boot.

Actually what I did is use this as previously mentioned:

Toggle* toggle = new TouchToggle[as];

Then within my library I have a function called “Begin”, which states the initial state of the toggle on startup, so within the main sketch setup function. This is where the pin is passed to the library.

 for(int i = 0; i <= n; i++) {
  //NOTE THIS IS JUST HERE FOR TESTING. THE VALUE OF toggleState & toggleLast will come from 
source that stores where the toggle was at before the board was turned off. 

  toggleState[i], toggleLast[i] = HIGH; 

  toggle[i].Begin(togglePin[i], toggleState[i]);
 }
  toggleState[i], toggleLast[i] = HIGH;

Why are you abusing the comma operator? Even if that works (which I doubt), it is MUCH clearer to set the current and last state to known values INDEPENDENTLY.

If toggle[ i ] is a pointer, you do not call its methods using the . operator. You use the -> operator.