I have classes which handle animations for a game.
I'm using the FastLED libary by Daniel Garcia in version 3.4.0
Basicly there is a list of actions (set an ALED's color, play an audiofile, wait,..) for each animation.
For this the ActionALED class needs access to the CRGB leds[numLEDs] array, to not have the same pointer in each instance it is a static member CRGB ActionALED::leds.
Which works great for ledsSize but results in a zero for leds.
Workaround:
To make it work I created a static method to set the static members within ActionALED which is called within setup() before I call FastLED.addLeds:
I would prefer the static members to be static const and not to set them twice though. I do not understand why CRGB ActionALED::leds = leds; does not set the static class member correctly.
I'm scratching me head trying to understand what the above line is meant to accomplish. What it appears to actually do is create a pointer named "leds" that points to itself, which makes no sense. Or, perhaps it creates a pointer set to whatever value is currently present in leds, which would, initially, be 0x0000.
I wouldn't worry about it since we're missing much context due to incomplete code being posted. If @ralfweniger ever bothers to comply with my request for a complete example, then it will be easier to tell.
I have produced a minimal version but this works as intended.. and I can not reproduce the error with it.
While doing so I found some other unexpected behaviour (see bellow)
In this example I switched the type from CRGB* to int* but the initialization is the same.
class StaticTest {
public:
StaticTest() {
Serial.print("StaticTest::testArray: "); Serial.println((size_t)testArray);
}
void doIt() {
testArray[0] = 5;
}
private:
static int* testArray;
};
int globalArray[5];
int* StaticTest::testArray = globalArray;
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
Serial.print("globalArray: "); Serial.println((size_t)globalArray);
StaticTest* staticTest = new StaticTest();
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED_BUILTIN, millis()%1000 < 500);
delay(20);
}
This will result in: "globalArray: 321
StaticTest ::testArray: 321"
The Number "321" is an address so it will be different after each compilation but both variables must have the same address.
In the original code the static array member will be 0 but the global array has some address.
So I guessed that arrays of objects will be allocated somewhere in the background before setup() is called. So I added an testObject and switched the types of the global and the static member to TestObject* but the error does not show.
Unexpected behaviour:
If in setup() staticTest is initialized like this: "StaticTest staticTest();" instead of "StaticTest* staticTest = new StaticTest();" the constructor will not be called or at least the serial Output from it will not appear.
That line is outside the class in global scope. It initializes the static member with a previously initialized array.
const uint8_t numLEDs = 10;
CRGB leds[numLEDs];
CRGB* ActionALED::leds = leds; //this does not work
uint8_t ActionALED::ledsSize = numLEDs; //this does
If I do not set the static array again in setup() via ActionALED::setLeds(leds, numLEDs); (code in original post) the address held by the static member is still zero.
I tried to reproduce the error in a minimal example but that works.. I could post the complete code but that is anything but minimalistic.
Creates a local variable on the stack. The space is allocated and the constructor called before your code in setup() executes. So, that happens before the call to 'Serial.begin(9600);'. Thus, nothing gets printed.
BTW, there's no good reason to use such a slow baud rate for Serial. Bump it to 115200.
Behavior like you're seeing with your big code is often due to overwriting an array boundary or dereferencing an incorrectly-set pointer. Try looking for anything like that.
Also, there's no real advantage to initializing your private static pointer the way you're trying to. Why not just provide a public, static 'setter' function for that and call it from setup()?
So parameterless construtors will be called wenn the scope is reached regardless of their positionin code? But constructors with non const parameters must be called at runtime still?!
Well I would prefer the static members to be const. Since I do not intend to change the number of LEDs mid runtime.
However I do not know whether it is considered fine to write on the elements of an array via a const pointer.
I will search for the memory problem. I guess i know where to look for it.
I don't understand what you mean or how you came to that conclusion. I'd have to say all constructors are run at "runtime" because they are, by definition, executable code.
will create a local variable on the stack and the constructor of StaticTest will be called before the body of setup() is executed while the initialization in code comes after Serial.begin(). Since Constructors with non constant parameters can't be executed before all the variable parameters are ready the execution must take place in the same order as was written in code.
Yes, the code did actually looked like that before all the nonsense with the zero pointer started.
This is exactly why I used that initialization method and not a static setter.
DOES NOT create a local instance of your StaticTest class. It's a prototype for a function that takes no arguments and returns an object of the StaticTest class.
Well, then since the small example works and the big code doesn't, it seems there's an error in the latter that probably only you will be able to find.
So the syntax creates a function header instead of creating a local object?
Like this but within the scope of setup():
void printColor(CRGG& c); //<- function header
void setup() {
Serial.begin(9600); //I like the default :P
printColor(CRGG::Black);
}
void loop() {
// put your main code here, to run repeatedly:
}
void printColor(CRGG& c) { //<- function body
Serial.print(c.r);
Serial.print(F("/"));
Serial.print(c.g);
Serial.print(F("/"));
Serial.println(c.b);
}