Home > Enterprise >  C array size depending on variable value known at compilation time
C array size depending on variable value known at compilation time

Time:12-25

I am working on a library to read Serial data from an electric counter. The counter can transmit up to 105 different tags depending on your contract. Because this library could be on lightweight and not powerful embedded systems (typically arduino or esp8266), I am trying to reduce the memory usage to the bare minimum.

To read each tag, my library has a buffer with its lenght equal the biggest tag value lenght possible (ranging from 2 bytes to 94 bytes). I would like my library to automatically set the buffer size to the correct value kowning that the user is able to indicate which tag he wants with #define instructions in its main file.

main.cpp

#define BASE
#define HCHP
#define OPTARIF

#include "LinkyTIC.h"
#include <SoftwareSerial.h>

SoftwareSerial LinkySerial(13, 15);
LinkyTIC linky(LinkySerial);

void setup() {
}

void loop() {
  if(linky.read()){
    int base = linky.GetBASE();
    int hchp = linky.GetHCHP();
    Serial.println(base);
    Serial.println(hchp);
  }
}

What I tried :

In my header file, I used a chain of #ifdef to keep the maximum lenght, but I get the error "the value of variable "max_lenght" cannot be used as a constant". I tried to cast the value to a constant but it didn't work either.

header.h

uint8_t max_lenght = 8;

#ifdef ADCO
    uint8_t max_lenght = max_lenght > 12 ? max_lenght : 12;
#endif
#ifdef OPTARIF
    uint8_t max_lenght = max_lenght > 4 ? max_lenght : 4;
#endif
#ifdef ISOUSC
    uint8_t max_lenght = max_lenght > 2 ? max_lenght : 2;
#endif
#ifdef BASE
    uint8_t max_lenght = max_lenght > 9 ? max_lenght : 9;
#endif
...

class LinkyTIC {
    public:
         // unrelated stuff

    private:
        // unrelated stuff

        char _buffer_tag[8];            // buffer for the tag name


>>>>    char _buffer_date[max_lenght];          // buffer for the (optional) tag date. Must be the same lenght as value because we can't know in advance if it s going to be a date or a value
                          ^^^^^^^^^^  the value of variable "max_lenght" cannot be used as a constant
>>>>    char _buffer_value[max_lenght];         // buffer for the tag value
                           ^^^^^^^^^^ the value of variable "max_lenght" cannot be used as a constant


        char _buffer_checksum[1];       // buffer for the tag checksum

        char _checksum;

        char* _buffers[4] = {_buffer_tag, _buffer_date, _buffer_value, _buffer_checksum};
        uint8_t _buffer_reference_index;
        uint8_t _buffer_index;
...
}

The value of max_lenght can thus be known at compile time, but I can't figure out a way to create my array using its value.

What would be the best way to achieve what I want to do ? Because flash is also an issue, I would rather avoid using std::vector.

CodePudding user response:

There are a number of options using preprocessor macros and #if statements, depending on what your requirements are, which you have not clearly stated. One example is:

#define MaximumLength 8

#if defined ADCO
    #if MaximumLength > 12
        #undef MaximumLength
        #define MaximumLength 12
    #endif
#endif
#if defined OPTARIF
    #if MaximumLength > 4
        #undef MaximumLength
        #define MaximumLength 4
    #endif
#endif
#if defined ISOUSC
    #if MaximumLength > 2
        #undef MaximumLength
        #define MaximumLength 2
    #endif
#endif
#if defined BASE
    #if MaximumLength > 9
        #undef MaximumLength
        #define MaximumLength 9
    #endif
#endif

uint8_t max_length = MaximumLength; // If this variable is still desired.
…

        char _buffer_date[MaximumLength];

CodePudding user response:

First of all you need to define your variable either const or consexpr. Since you code already has error, you can't redefine variable with same name, I'm not sure what you intended to do, but something like this should work:

#include <cstdint>
#include <iostream>
#include <algorithm>

// Uncomment line(s) below to check behavior
//#define ADCO
//#define OPTARIF
//#define ISOUSC
//#define BASE

const uint8_t def_max_length = 8;

#ifdef ADCO
    const uint8_t max_length = std::max<uint8_t>(def_max_length, 12);
#elif defined(OPTARIF)
    const uint8_t max_length = std::max<uint8_t>(def_max_length, 4);
#elif defined(ISOUSC)
    const uint8_t max_length = std::max<uint8_t>(def_max_length, 2);
#elif defined(BASE)
    const uint8_t max_length = std::max<uint8_t>(def_max_length, 9);
#else
    const uint8_t max_length = def_max_length;
#endif

char buffer[max_length];

int main()
{
    std::cout << std::size(buffer) << std::endl;
}

If you need to check all defines independently, then you can define one variable for each define, something like:

#ifdef ADCO
    const uint8_t max_length_adco = 12;
#else
    const uint8_t max_length_adco = 0;
#ednif

And then just get maximum of all these variables:

const uint8_t max_length = std::max({def_max_length, max_length_adco, max_length_optarif, max_length_isousc, max_length_base});
  • Related