Code optimization: Avoiding compilation of unused constants

I'd like to write a C++ LED library that supports WS2812 APA104 etc. that does notwaste any SRAM/FLASH.

For each LED type I need to track constants for the number of cycles high/low the values Zero and one are. If someone links the library only for WS2812, I don't want the APA104 constants to use any memory.

How could I define those so I can use them as needed?

One way I'm thinking is with macros but I find that ugly:

#define WS2812_LEDSTRIP

char pixels[numPixels*3];
ledStrip strip1(WS2812, numPixels, portNumber, portDDR, portPin);

loop() {
  strip1.show(pixels);
  strip1.delay();
}

I'd like the user to not have to use a define...
This also means the use can't use 2 types of LED strips at the same time.

The class has to use predefined values for timing aka:
__builtin_avr_delay_cycles(CYCLES(WS2812B_1L));

The above solution would be:
__builtin_avr_delay_cycles(CYCLES(LEDSTRIP_1L));
where LEDSTRIP_1L is set the the WS2812B's value.

I'd rather have constants...

ledstrip::ledstrip(enum striptype,...) 
{
  switch(striptype) {
  case WS2812:
    this->ledstrip_1l = WS2812B_1L;
   case APA104:
    this->ledstrip_1l = WS2812B_1L;
   default:
#error boom.
   }
}

However I'd like to not use any SRAM for this->ledstrip_1l.

I could also create child classes maybe?

ledStrip_WS2812 : ledstrip {
  const uint8_t ledstrip_1l = WS2812B_1L;
}
ledStrip_APA104 : ledstrip {
  const uint8_t ledstrip_1l = APA104_1L;
}

If the APA104 class is not instantiated, then would this work maybe?

If you have general ideas and concepts to share on how to write optimal code for this, I'd like to hear it. :slight_smile:

There is an implication that the WS2812 constants and the APA104 constants cannot possibly both be used at run-time. Is that correct? Would someone ever have both connected to one processor?

If you can set it up so that the compiler can see that you’re not using a function, then that function won’t be compiled int your code. For a simple example, #include <string.h> then use one of those functions in your code. Look at the code size reported when it compiles. Now use two of the string functions and see how much bigger your code gets. It does get bigger but only one function bigger. Therefore when the string.h library is compiled with your code it only includes the functions you are actually using.

Constants, like numerical constants, can be defined with the const keyword but even if you don’t use the const keyword, the compiler will agressively track down the constants in your code and optimise them away.

const int x = -123;
int y = 6;
void setup() {
  Serial.begin(9600);
  Serial.print("Result = ");
  Serial.prinltn(x/y);
}
void loop() {}

If you were to look at the assembler code generated from this, you would see that there is no division code. The compiler will detect that x and y are constants and it will do the integer division at compile-time, so the running code on the Arduino is equivalent to Serial.println(-20); SRAM is not used at all.

I want to allow the user to mix’n’match LED strip types if needed.

I want the constants/code to not be compiled in, NOR have the code duplicated for each LED strip type if not necessary.

There is one huge catch: " __builtin_avr_delay_cycles(count);" requires count to be a compile time constant, so “int const count = 32” does NOT compile. using “#define count 32” compiles.

I’ve come up with a partial solution so far:

template <int cst> class foo {
   foo() { __builtin_avr_delay_cycles(cst) };
}

class ws2812b : public foo<32>;
class apa104 : public foo<128>;

apa104 mystrip;
loop() {
  mystrip.show();
}

I now have an issue with finding a generic way to select the DDR register to configure the I/O.