Vermeidung von dynamischer Array-Erstellung möglich?

Hallo zusammen,
ich habe folgende Klasse, der per Referenz ein Array übergeben wird:

class ButtonDebounce{
  public:
    template<size_t N>
    ButtonDebounce(const uint8_t (&pinArray)[N]) : pin2Debounce{pinArray}{
      shiftReg = new uint8_t[N]{};
      totalPins = N;
    }

    // ...

  private:    
    uint8_t totalPins = 0;
    uint8_t* shiftReg = nullptr;
    const uint8_t* pin2Debounce = nullptr;
};

Das Ganze wird kann folgendermaßen aufgerufen:

const uint8_t testPins[] = {10,20,30,40,50,60,70,80,99,111,222};
ButtonDebounce debounce(testPins);

Das Byte-Array (hier 'testPins') kann beliebig lang angegeben werden, dessen Größe ist ja dann zur Kompilierungszeit bekannt. Ich benötige nun innerhalb der Klasse ein weiteres Byte-Array (hier 'shiftReg'), welches die Größe des übergebenen Arrays besitzen muss.
Meine Frage lautet nun: Ist es möglich - und falls ja, wie? - das Array shiftReg bereits zur Kompilierungszeit zu reservieren/initialisieren ohne die Krücke über "new" gehen zu müssen?

Vielen Dank und ein schönes Wochenende :slight_smile:

Eine Möglichkeit wäre folgendes:


template <size_t N> class ButtonDebounce {
public:
  explicit ButtonDebounce(const uint8_t* pinArray) : pin2Debounce {pinArray} {}

  // ...

  void show(Print &stream) {
    for (size_t i {0}; i < totalPins; ++i) {
      stream.print(pin2Debounce[i]);
      stream.print(" ");
    }
  }

private:
  uint8_t totalPins {N};
  uint8_t shiftReg[N];
  const uint8_t* pin2Debounce = nullptr;
};


void setup() {
  Serial.begin(115200);

  const uint8_t testPins[] = {10, 20, 30, 40, 50, 60, 70, 80, 99, 111, 222};
  ButtonDebounce<sizeof(testPins) / sizeof(testPins[0])> debounce(testPins);

  debounce.show(Serial);
}

void loop() {}
1 Like

Danke für den Denkanstoß :slight_smile:
Auf die Idee das Template über die gesamte Klasse zu stülpen bin ich noch gar nicht gekommen. Leider "verkompliziert" das den Aufruf der Objekterstellung etwas, wobei der Teil "/ sizeof(testPins[0])" obsolet ist, der Wert ergibt im vorliegenden Fall immer 1.

Ja .. aber nur, wenn der Typ immer char / unsigned char ist ....

Es könnte ja auch mal so aussehen:

template <typename T, size_t N> class ButtonDebounce {
public:
  explicit ButtonDebounce(const T* pinArray) : pin2Debounce {pinArray} {}

  // ...

  void show(Print& stream) {
    for (size_t i {0}; i < totalPins; ++i) {
      stream.print(pin2Debounce[i]);
      stream.print(" ");
    }
  }

private:
  uint8_t totalPins {N};
  T shiftReg[N];
  const T* pin2Debounce = nullptr;
};

void setup() {
  Serial.begin(115200);
  const int testPins[] = {10, 20, 30, 40, 50, 60, 70, 80, 99, 111, 222};
  ButtonDebounce<int, sizeof(testPins) / sizeof(testPins[0])> debounce(testPins);

  debounce.show(Serial);
}

void loop() {}

Aber natürlich kannst Du das "/ sizeof(testPins[0])" weglassen. Kommt halt darauf an, wie flexibel und möglichst "fehlerunanfällig" Du den Code machen möchtest...

Da gebe ich dir vollkommen recht... "könnte" - kann es aber nicht, weil ich ja bei der Übergabe festgezurrt habe das 8-Bit-Zellen übergeben werden müssen. Also ist das ehr eine Verschlimmbesserung und Speicherverschwendung das Ganze dermaßen variabel zu gestalten.

Nein!

Natürlich ist die Verwendung von N besser als "sizeof(testPins) / sizeof(testPins[0])".
Aber Speicherverschwendung, nein, da irrst du.

Warum sollte ich auf die Idee kommen 16-, oder gar 32-Bit-Zellen in den Speicher zu stellen oder zumindest die Möglichkeit dazu zu geben, wenn 8 Bit zur Darstellung der benötigten Werte vollkommen ausreichend sind?

Warum sollte man eine Spezialisierung verwenden, wenn man doch genauso gut eine allgemeine und auch kostenlose Lösung verwenden kann?

Merke:
Vorzeitige Spezialisierungen behindern die Wiederverwendung und führen so im Endeffekt zu Codeduplikaten.

Wenn du schon 222 Pins da hast, was verhindert da den Pin mit der Nummer 256?

Im Idealfall verhindert dies das Codeverständnis des Programmierers - und als "AddOn" die Warnungen / Fehlermeldungen, die der Compiler einem um die Ohren haut :wink:
Ich verstehe natürlich deinen Ansatz, aber wenn das Array von vorn herein mit unsigned 8 Bit deklariert wird bist du eh in den Poppes gekniffen wenn du Werte außerhalb dieses Bereichs definierst.

Ich sehe schon, du möchtest gerne die Arraygröße per template verallgemeinern, aber nicht den unterliegenden Type.

Alles gut: Dein Wille ist dein Himmelreich.

Übrigens, hier eine Variante, wo dein 8 Bit Array nicht mehr ausreicht:

#include <Streaming.h> // die Lib findest du selber ;-)
Print &cout = Serial; // cout Emulation für "Arme"

template<typename T, size_t N> constexpr size_t arrayCount(T(&)[N]) {return N;}  /// remove_const
template<typename T> struct remove_const {using Type = T;};
template<typename T> struct remove_const<T const> {using Type = T;};


template<typename ArrayType>
class ButtonDebounce
{
  public:
    ButtonDebounce(ArrayType &pinArray): pinArray{pinArray} {}

    void tuwas()
    {
      cout << __PRETTY_FUNCTION__ << endl;
      cout << F("Anzahl: ") << arrayCount(pinArray) << endl;
      for(auto p : pinArray) cout << F("Pin: ") << p << endl;
      cout << F("-----------")  << endl;
    }
    
  private:
    ArrayType &pinArray;
    remove_const<ArrayType>::Type shiftReg;
};

const uint8_t testPins[] = {10, 20, 30, 40, 50, 60, 70, 80, 99, 111, 222};


ButtonDebounce<decltype(testPins)> debounce{testPins};


void setup()
{
  Serial.begin(9600);
  cout << F("Start: ") << F(__FILE__) << endl;
  debounce.tuwas();
}

void loop()
{

}

Nachtrag/Edit:
Den const type qualifier von ArrayType entfernt um einen veränderlichen(none const) Type für das shiftReg Array zu erschaffen.
Denn das war ja eins der Ziele: shiftReg ohne new

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.