How to do these statements inside a FOR loop?

gatheround

Well-known member
Hey everyone, I'm trying to simplify my code and make the following statements inside a nice/easy FOR loop. I'll be manipulating these variables lots, so being able to address them like an array would help out lots. This code is to change parameters inside a FAUST audio object, but I figure the question is more of a general programming question.

Code:
  test.setParamValue("enable0", 1);     
  test.setParamValue("enable1", 1);      
  test.setParamValue("enable2", 1);   
  test.setParamValue("enable3", 1);     
  test.setParamValue("enable4", 1);     
  test.setParamValue("enable5", 1);     
  test.setParamValue("enable6", 1);    
  test.setParamValue("enable7", 1);    
  test.setParamValue("enable8", 1);     
  test.setParamValue("enable9", 1);  
  test.setParamValue("enable10", 1);   
  test.setParamValue("enable11", 1);

I tried making an array of strings with all of the param names and iterating through that, but it didn't seem to work. Any suggestions? would love to do something like:
Code:
  test.setParamValue(enable[i], 1);
 
Any suggestion depends on knowing whether test.setParamValue() retains the string or just uses it at the time of that function and retains no memory of it. If it does keep the string for future use, we need to know whether setParamValue() makes a copy of the data, or just keeps a pointer / reference (so it can later access the string at its original memory location).

The first 2 cases are pretty easy. If the string isn't kept, or if setParamValue() makes a copy, then your code doesn't keep to worry about keeping the string data in memory after setParamValue() returns. You can just allocate a temporary buffer memory for the string as a local variable and use sprintf() to put the string want into the temporary buffer. It would look something like this:

Code:
  for (int i=0; i <= 11; i++) {
    char buf[12];
    snprintf(buf, sizeof(buf), "enable%d", i);
    test.setParamValue(buf, 1);
  }

The case where a function keeps a pointer or reference to the string is harder. You can't just use a temporary buffer, for two reasons. #1: each loop iteration will overwrite the buffer, so if setParamValue() kept the address of the buffer and something tries to later access it, all pointers will be to the same memory which got overwritten with the last data. #2: after your function returns, the local variable no longer exists and other future local variables will overwrite it, so any future attempt to read the string from the original location will (probably) not even have the last iteration, but other data from the memory being reused.

There are a few ways to deal with the situation where code you gave a string will later read the memory. They fall into 2 categories. You can create global or static buffer(s) which will hold all 11 strings. Variable with global scope, or local variables with "static", exist for as long as your program runs. So for could just make the buffer "static" and 11 times larger, and adjust the code to use it's only part of the buffer. Something like this:

Code:
  for (int i=0; i <= 11; i++) {
    static char buf[12 * 11];
    snprintf(buf + i * 12, 12, "enable%d", i);
    test.setParamValue(buf + i * 12, 1);
  }

The other basic way is to use malloc() or C++ new to request allocation of chunks of memory. The Arduino String class also does this automatically, so it's popular for functions which accept a String rather than "const char *" as their input.

First you'd want something like this as a global variable
Code:
char * paramlist[11];

Then you'd use code like this:

Code:
  for (int i=0; i <= 11; i++) {
    if (paramlist[i] == NULL) {
      paramlist[i] = (char *)malloc(12);
    }
    if (paramlist[i] != NULL) {
      snprintf(paramlist[i], 12, "enable%d", i);
      test.setParamValue(paramlist[i], 1);
    }
  }

Presumably you would add code which later uses the paramlist[] pointers to free the allocated memory after you're sure nothing will need to access it any longer.

But of course everything is so much simpler if you can be sure test.setParamValue() doesn't try to access the string later, or if it does it makes a copy. You only need to go to this extra effort to keep the data preserved in memory if whatever code you're calling will keep the reference to the original data and try to access it later.
 
Hi Paul, looks like the first method worked! Thank you.

Why can't it be even simpler like:

Code:
 String enable[] = {
   "enable0",
   "enable1",
   "enable2",
  "enable3",
  "enable4",
   "enable5",
  "enable6",
  "enable7",
  "enable8",
  "enable9",
 "enable10",
  "enable11"
 };

and

Code:
 for (int a = 0; a < 13; a++) {
   test.setParamValue(enable[a], 1);
}

Just wondering what is happening that is so different, am I not just passing a string to test.setParamValue for it to match with a param named the same thing?

Thanks for your time,



Any suggestion depends on knowing whether test.setParamValue() retains the string or just uses it at the time of that function and retains no memory of it. If it does keep the string for future use, we need to know whether setParamValue() makes a copy of the data, or just keeps a pointer / reference (so it can later access the string at its original memory location).

The first 2 cases are pretty easy. If the string isn't kept, or if setParamValue() makes a copy, then your code doesn't keep to worry about keeping the string data in memory after setParamValue() returns. You can just allocate a temporary buffer memory for the string as a local variable and use sprintf() to put the string want into the temporary buffer. It would look something like this:

Code:
  for (int i=0; i <= 11; i++) {
    char buf[12];
    snprintf(buf, sizeof(buf), "enable%d", i);
    test.setParamValue(buf, 1);
  }

The case where a function keeps a pointer or reference to the string is harder. You can't just use a temporary buffer, for two reasons. #1: each loop iteration will overwrite the buffer, so if setParamValue() kept the address of the buffer and something tries to later access it, all pointers will be to the same memory which got overwritten with the last data. #2: after your function returns, the local variable no longer exists and other future local variables will overwrite it, so any future attempt to read the string from the original location will (probably) not even have the last iteration, but other data from the memory being reused.

There are a few ways to deal with the situation where code you gave a string will later read the memory. They fall into 2 categories. You can create global or static buffer(s) which will hold all 11 strings. Variable with global scope, or local variables with "static", exist for as long as your program runs. So for could just make the buffer "static" and 11 times larger, and adjust the code to use it's only part of the buffer. Something like this:

Code:
  for (int i=0; i <= 11; i++) {
    static char buf[12 * 11];
    snprintf(buf + i * 12, 12, "enable%d", i);
    test.setParamValue(buf + i * 12, 1);
  }

The other basic way is to use malloc() or C++ new to request allocation of chunks of memory. The Arduino String class also does this automatically, so it's popular for functions which accept a String rather than "const char *" as their input.

First you'd want something like this as a global variable
Code:
char * paramlist[11];

Then you'd use code like this:

Code:
  for (int i=0; i <= 11; i++) {
    if (paramlist[i] == NULL) {
      paramlist[i] = (char *)malloc(12);
    }
    if (paramlist[i] != NULL) {
      snprintf(paramlist[i], 12, "enable%d", i);
      test.setParamValue(paramlist[i], 1);
    }
  }

Presumably you would add code which later uses the paramlist[] pointers to free the allocated memory after you're sure nothing will need to access it any longer.

But of course everything is so much simpler if you can be sure test.setParamValue() doesn't try to access the string later, or if it does it makes a copy. You only need to go to this extra effort to keep the data preserved in memory if whatever code you're calling will keep the reference to the original data and try to access it later.
 
Hi Paul, looks like the first method worked! Thank you.

Why can't it be even simpler like:

Code:
 String enable[] = {
   "enable0",
   "enable1",
...
  "enable10",
  "enable11"
 };

and

Code:
 for (int a = 0; a < 13; a++) {
   test.setParamValue(enable[a], 1);
}

Just wondering what is happening that is so different, am I not just passing a string to test.setParamValue for it to match with a param named the same thing?

Thanks for your time,

It depends on how much the difference is between c strings and c++ std::string matters.
You may need to use
Code:
test.setParamValue(enable[a].c_str(), 1);
for it to work, that would convert the std::string to a c format string.

Why use c style strings rather than std::string? They are faster and more memory efficient, both good things on small memory constrained embedded systems. The differences are small enough that these days unless you're really pushing the limits it's not enough to matter, use whichever is easier and creates cleaner code for your given situation. But expect to see lots of people, especially people with more experience of smaller/slower parts avoiding std::string whenever possible.
 
Back
Top