How to refernce variable length array with pointer

project description: I have an esp8266 that is receiving a string representing a color pattern of rgb values with a time for transitioning to the next color.
example format: "0,0,0,1000;255,0,0,1000;"
that is two values with 1000ms transition between each. I am parsing the incoming data so that, after it has been parsed I can loop over the values and update the gpio pins.

My issue: I am having trouble properly saving the variables in a dynamic length array and creating a pointer to that array. The pointer to the array has the same memory addresses but not the same values (at least for the first index that I am printing)

my approach: parse the incoming string so that I save each tick in the pattern; "255,0,0,1000" in an object, and each of those objects in a dynamic length array that I can point to in the main loop

goal: I need to be able to save the incoming string in a format that I can loop over, without blocking potential future incoming values (if I want to run a new color pattern that is received over wifi)

I have written the code in a replit: https://repl.it/@jordanklaers/objectArrayValueReferencing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> 

class color {
  public:
    int red;
    int green;
    int blue;
    int transitionTime;
    void printObj() {
      std::cout << "red: " << red << " - green: " << green << " - blue: " << blue << " - time: " << transitionTime;
    }
};

color *patternListObjects;
int patternLength = 0;

void parseTickValues(char *tick, int patternIndex, color *list) {
  char *value;
  color TickObj;
  //red green blue time seperated by ","
  value = strtok (tick,",");
  for (int i = 0; i < 4; i ++) {
    // std::cout << atoi(value) << "\n";
    if (i == 0) {
      TickObj.red = atoi(value);
    } else if (i == 1) {
      TickObj.green = atoi(value);
    } else if (i == 2) {
      TickObj.blue = atoi(value);
    } else if (i == 3) {
      TickObj.transitionTime = atoi(value);
    }
    value = strtok (NULL, ",");
  }
  //add color obj to the list
  list[patternIndex] = TickObj;
}



void splitPatternTicks(char *pattern, int ticks) {
      char *colorsAtTick;
      //im told this is bad? *colorsAtTickArray[ticks];
      char *colorsAtTickArray[ticks];
      //split pattern on ";" into chars
      colorsAtTick = strtok (pattern,";");
      while (colorsAtTick != NULL)
      {
        //save the chars into an array
        colorsAtTickArray[patternLength] = colorsAtTick;
        patternLength ++;
        colorsAtTick = strtok (NULL,";");
      }
      
      color list[patternLength];// = {};
      for (int i = 0; i < patternLength; i ++) {
        //loop over the array of char to further split and save values
        //colorsAtTickArray ex. "255,0,0,1000"
        parseTickValues(colorsAtTickArray[i], i, list);
      }
      patternListObjects = list;
      //Works here:
      std::cout << "\nprinting right after assignment: \n" << 
      &patternListObjects[0].red << "\n" << 
      &patternListObjects[0].green << "\n" << 
      &patternListObjects[0].blue << "\n\n" << 
      patternListObjects[0].red << " " << 
      patternListObjects[0].green << " " << 
      patternListObjects[0].blue << "\n\n";
    }


int main() {
  //the string I am receiving, it could vary in length
  //it represents rgb values with a time in ms to transition to the next color
  char string[42] = "0,0,0,1000;25,25,25,1000;150,150,150,1000";
  char *stringPointer = string;
  splitPatternTicks(stringPointer, 3);

  //fails here:
  std::cout << "printing through the refernce: \n" << 
  &patternListObjects[0].red << "\n" << 
  &patternListObjects[0].green << "\n" << 
  &patternListObjects[0].blue << "\n\n" << 
  patternListObjects[0].red << " " << 
  patternListObjects[0].green << " " << 
  patternListObjects[0].blue << "\n\n";
}
Why dynamic length array? Why not std::vector<color> ?

Why global variables? They are very restrictive.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// project description: I have an esp8266 that is receiving a string
// representing a color pattern of rgb values with a time for transitioning
// to the next color.
// example format: "0,0,0,1000;255,0,0,1000;"
// that is two values with 1000ms transition between each.
// I am parsing the incoming data so that, after it has been parsed I can loop
// over the values and update the gpio pins.
// My issue: I am having trouble properly saving the variables in a dynamic
// length array and creating a pointer to that array.
// The pointer to the array has the same memory addresses but not the same
// values (at least for the first index that I am printing)
// my approach: parse the incoming string so that I save each tick in the
// pattern; "255,0,0,1000" in an object, and each of those objects in a dynamic
// length array that I can point to in the main loop
// goal: I need to be able to save the incoming string in a format that I can
// loop over, without blocking potential future incoming values (if I want
// to run a new color pattern that is received over wifi)
#include <iostream>
#include <sstream>
#include <string>
#include <vector>


// ----------------------------------------------------------------------------
//                                  CLASS COLOR
// ----------------------------------------------------------------------------
#ifndef CLASS_COLOR
#define CLASS_COLOR

struct color {
    int red {};
    int green {};
    int blue {};
    int transition_time {};

    color() = default;
    color(int red_arg, int green_arg, int blue_arg, int transition_time_arg);
    color(std::string s);

// friend:
    friend std::istream& operator>>(std::istream& is, color& rhs);
    friend std::ostream& operator<<(std::ostream& os, const color& rhs);
};


color::color(int red_arg, int green_arg, int blue_arg, int transition_time_arg)
    : red { red_arg }
    , green { green_arg }
    , blue { blue_arg }
    , transition_time { transition_time_arg }
{
}


color::color(std::string s)
{
    std::istringstream iss { s };
    iss >> red >> green >> blue >> transition_time;
}


std::istream& operator>>(std::istream& is, color& rhs)
{
    is >> rhs.red >> rhs.green >> rhs.blue >> rhs.transition_time;
    return is;
}


std::ostream& operator<<(std::ostream& os, const color& rhs)
{
    os << "red: " << rhs.red << " - green: " << rhs.green
       << " - blue: " << rhs.blue << " - time: " << rhs.transition_time;
    return os;
}

#endif // CLASS_COLOR


// ----------------------------------------------------------------------------
//                     STANDALONE FUNCTION PROTOTYPES
// ----------------------------------------------------------------------------
std::istringstream receiveColors(/* type from ? */);
std::string extractSingleColor(std::istringstream& iss);
std::vector<color> fillVectorColorFromStream(std::istringstream& iss);
template <typename T>
void
printVec(const std::vector<T>& v,
         char after_element = ' ',
         char end_of_elements = '\0');


// ----------------------------------------------------------------------------
//                                   MAIN()
// ----------------------------------------------------------------------------
int main()
{
    auto iss { receiveColors(/* */) };
    std::vector<color> v { fillVectorColorFromStream(iss) };
    printVec(v, '\n');
}


std::istringstream receiveColors(/* type from ? */)
{
    // Create an istringstream from your source data (file, ... ):
    std::istringstream iss { "0,0,0,1000;25,25,25,1000;150,150,150,1000" };
    return iss;
}


std::string extractSingleColor(std::istringstream& iss)
{
    std::string s;
    std::getline(iss, s, ';');
    return s;
}


std::vector<color> fillVectorColorFromStream(std::istringstream& iss)
{
    std::vector<color> v;

    // 's' needs an inital value, otherwise the loop doesn’t start:
    for ( std::string s { extractSingleColor(iss) };
          !s.empty();
          s = extractSingleColor(iss) )
    {
        v.push_back( color(s) );
    }

    return v;
}


template <typename T>
void
printVec(const std::vector<T>& v, char after_element, char end_of_elements)
{
    for(const auto& e : v) {
        std::cout << e << after_element;
    }
    if('\0' != end_of_elements) {
        std::cout << end_of_elements;
    };
}


Output:
red: 0 - green: 0 - blue: 0 - time: 0
red: 25 - green: 0 - blue: 0 - time: 0
red: 150 - green: 0 - blue: 0 - time: 0

At line 58 in splitPatternTicks() you create a local array. At line 64 you assign the global pointer patternListObjects to it. When splitPatternTicks() exits, the local array goes out of scope and patternListObjects is left pointing to unallocated memory.

As you note in your comments, variable length arrays are bad (because they are not standard C++).

It would be much cleaner to use vector<> and a method to read a color object from a stream. I'll assume that you don't know how to create >> and << operators to read/write colors from streams directly since you created the printObj() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <sstream>

using std::vector;
using std::istream;
using std::ostream;
using std::cin;
using std::cout;
using std::istringstream;
class color
{
  public:
    int red;
    int green;
    int blue;
    int transitionTime;
    istream &read(istream &is);
    void printObj()
    {
	std::cout << "red: " << red
		  << " - green: " << green
		  << " - blue: " << blue
		  << " - time: "
		  << transitionTime;
    }
};

color *patternListObjects;
int patternLength = 0;

istream &
color::read(istream &is)
{
    char comma1, comma2, comma3;
    is >> red >> comma1 >> green >> comma2 >> blue >> comma3 >> transitionTime;
    if (!(comma1== ',' && comma2 == ',' && comma3 == ',')) {
	// indicate that the conversion failed
	is.setstate(is.rdstate() | std::ios::failbit);
    }
    return is;
}

int
main()
{
    //the string I am receiving, it could vary in length
    //it represents rgb values with a time in ms to transition to the next color
    char string[42] = "0,0,0,1000;25,25,25,1000;150,150,150,1000";
    istringstream strm(string);

    vector<color> vec;
    color c;
    while (c.read(strm)) {
	char ch;
	vec.push_back(c);
	strm >> ch;		// read the semicolon
    }

    for (unsigned i=0; i<vec.size(); ++i) {
	vec[i].printObj();
	cout << '\n';
    }
}

Thank you so much everyone. I greatly appreciate that you pointed out why my approach was failing; the memory allocated in the function goes away.
You are correct, I dont know what streams are but thats exactly what I needed, something to google.


@keskiverto, I saw vectors, but didnt try that approach first because I read they are a bit tougher on memory and I am writing this program to be used on an ESP8266 so I figured I'd try the less memory intensive approach.
Regarding global variables, what do you mean "They are restrictive"? I thought of saving the parsed values to a global variable that could be looped over in the main loop. I though that by doing that, it would allow for new values to come in without being blocked. I imagined looping in a method called after they were parsed would cause some blocking. Quite possible that was an incorrect assumption.
I read [std::vectors] are a bit tougher on memory
If you know now many items are present ahead of time then you can preallocate space for them. Even if you just have a guess, that can help. http://www.cplusplus.com/reference/vector/vector/reserve/
@dhayden

I supposed I'll worry about memory if it becomes an issue, but thank you for the suggestion.

I had a few clarifying questions about the code you wrote.

line 21. is that syntax saying, there is a read method, where line 35/36 is the method? so on 35/36, the address of the color class’s read property is that following method?



Is the Istream read the same as this one? http://www.cplusplus.com/reference/istream/istream/read/


Because I don’t see a length for the second argument

you only create a color object once on line 56, but you add it to the end of the vector on line 59 n number of times. first guess is that color c is getting over written on each iteration, but Im assuming the color object that gets added to the vector is separated from the color c object that gets written into in the next iteration?

also, do we need line 10?
also, for line 39, how does it know how many chars to read for each variable?
line 21. is that syntax saying, there is a read method, where line 35/36 is the method?

Yes.

so on 35/36, the address of the color class’s read property is that following method?



Please clarify your problem, line 35 is the start of the read() method implementation. Note the color:: tells the compiler that this implementation is part of the color class.

Is the Istream read the same as this one?

No, it is the one starting at line 35 and ending at line 45. Perhaps you should look up function overloading?

you only create a color object once on line 56, but you add it to the end of the vector on line 59 n number of times. first guess is that color c is getting over written on each iteration,

Correct, but instead of guessing you should run your program with your debugger and single step through this method and see for yourself what is happening.
but Im assuming the color object that gets added to the vector is separated from the color c object that gets written into in the next iteration?

A copy of the object is inserted into the vector so the original is free to change anytime after the insertion.

also, do we need line 10?

If you properly scope those standard classes you don't need lines 8 through 13, ie: std::string, std::vector.

also, for line 39, how does it know how many chars to read for each variable?

The insertion operator>> will keep extracting "characters" until it encounters either a white space character or an invalid "character". In this case an invalid "character" is any non-digit character.

EDIT:
I supposed I'll worry about memory if it becomes an issue, but thank you for the suggestion.


Depending on the version of that embedded processor you may want to stay away from as many dynamic memory features as possible since that processor can be very memory (ram, sram) limited and sometimes it can be difficult to locate the memory exhaustion issues because they may not always occur.



Last edited on
Topic archived. No new replies allowed.