Constexpr als Argument in Funktion not allowed

Hallo, ich lerne gerade am Code vom Doc


const byte pinTaster {2};
const byte pinLed {12};
const byte pinHeartbeat {13};
const unsigned int BLINKPAUSE {1000};


void setup (void)
{
  pinMode(pinTaster, INPUT_PULLUP);
  pinMode(pinLed, OUTPUT);
  pinMode(pinHeartbeat, OUTPUT);
}

void loop (void)
{  
  steuerung(pinTaster, pinLed);
  heartbeat(pinHeartbeat, 500);
}


void steuerung (const byte pinT, const byte pinL)
{
  enum class state : byte {EIN, PAUSE_EIN, AUS, PAUSE_AUS, NICHTS};
  static state zustand {state::NICHTS};
  static unsigned long zeitmerker {0}; 
  
  switch (zustand) {
    case state::NICHTS:
                if (updateTaster(pinT) ) {
                  zustand = state::EIN;
                }
                break;

    case state::EIN:
                digitalWrite(pinL, HIGH);
                zeitmerker = millis();
                zustand = state::PAUSE_EIN;
                break;

    case state::PAUSE_EIN:
                if ( millis() - zeitmerker >= BLINKPAUSE) {
                  zustand = state::AUS;
                }
                break;

    case state::AUS:
                digitalWrite(pinL, LOW);
                zeitmerker = millis();
                zustand = state::PAUSE_AUS;
                break;

    case state::PAUSE_AUS:
                if ( millis() - zeitmerker >= BLINKPAUSE) {
                  zustand = state::NICHTS;
                }
                break;
  }
}


bool updateTaster (const byte pin)
{
  static unsigned long last_ms {0};
  static bool state {false};

  if (millis() - last_ms >= 30) {
    last_ms = millis();
    state = !(digitalRead(pin));
  }
  return state;
}


void heartbeat (const byte pin, const unsigned int interval)  // Kontrolle ob Sketch blockiert
{
  static unsigned long lastMillis {0};
  static bool state {LOW};
  const unsigned long ms {millis()};
  
  if (ms - lastMillis >= interval) {
    lastMillis = ms;
    state = !state;
    digitalWrite(pin, state);
  }
}

Wenn ich die Argumente der Funktionen auf constexpr umstelle, kommt die Fehlermeldung
Constexpr Not allowed

Zum Beispiel


void steuerung (constexpr byte pinT, constexpr  byte pinL)
{

Auch Suchen hat mich nicht weitergebracht, warum das nicht erlaubt ist.
Es sind doch Konstanten?

Ist es einfach so, weil es so festgelegt wurde oder hat das einen logischen Grund?

Danke euch :slight_smile:

constexpr Ausdrücke müssen sollten zur Compilezeit auflösbar sein.
Funktionsparameter können/werden bei jedem Aufruf anders sein.

Diese beiden Anforderungen widersprechen sich.
Somit kann es keine constexpr Funktionsparameter geben.

Sind Funktionsparameter nicht.

const heißt nur "read only"
Auch etwas was read only ist, kann bei jedem Aufruf anders sein.

Wenn du Literale oder constexpressions in eine Funktion injizieren willst, kannst du das über Template Parameter machen.

1 Like

Ich hätte das so verstanden, dass dann nur Funktionsparameter erlaubt sind, die schon beim Übersetzen feststehen. Macht natürlich keinen Sinn Ist fragwürdig, dafür einen Funktionsparameter zu verwenden, aber dass es not allowed ist, war mir auch neu.

Ist auch zu unterscheiden zwischen dem Aufrufer der Funktion und dem Funktionsblock selbst:
const sagt dem Aufrufer, dass eine Konstante verwendet werden darf (aber nicht muss)
und der Funktion selbst, dass sie nicht verändert werden darf.
constexpr würde dem Aufrufer sagen, dass eine constexpr verwendet werden muss, und die Funktion selbst könnte beliebig optimiert werden, wie man das bei templates erleben kann. (Nach meinem Verständnis, wenn es denn so vorgesehen wäre)

Es gibt in C und C++ keine echten Konstanten.
Außer die recht modernen constexpr Ausdrücke in C++.
Es ist also nicht gut über Konstanten zu sprechen, oder in Konstanten zu denken, wenn es das nicht gibt.
Das führt nur zu Verwirrungen und logischen Fehlschlüssen, wie man hier im Thread gut sieht.

Beide Sprachen kennen const, was aber nicht Konstant bedeutet, sondern "read only".
Darum ist auch sowas erlaubt:
volatile const long irgendwas;
"irgendwas" ist hier für das Programm read only, kann aber jederzeit von Außerhalb geändert werden.

Was einer Konstanten recht ähnlich ist, sind Literale.
z.B. eingestreute magische Zahlen oder #define pi 3.14;

Ich wiederhole:
constexpr Ausdrücke müssen zur Compilezeit ausgewertet werden können.
Funktionsparameter werden zur Laufzeit verarbeitet.
Siehst du den Widerspruch!?!?!

Ich sehe einen Funktionsparameter, von dem der Compiler weiß, dass er schon beim Compilieren feststeht. Wie der Compiler das ausnutzt, würde ich ihm überlassen.
Dass das wie ein Template riecht, nur anders aussieht, stimme ich dir zu.
Unter dem Stichwort const vs. constexpr findet man genug, dein Hinweis, dass const eigentlich readonly bedeutet, ist auch hilfreich.

Ich sehe da keinen Funktionsparameter.
Ich glaube dem Compiler und sehe da ebenfalls eine Unmöglichkeit, ein Paradox.
Einen Widerspruch.
Wie er das ausnutzt?
Er haut es dir/mir/progger um die Ohren.

pinT und pinL sollen hier zur Laufzeit übergeben/besetzt werden.
constexpr Ausdrücke müssen zur Compilezeit ausgewertet werden können.

void steuerung (constexpr byte pinT = 42, constexpr  byte pinL = 44)
{
}

Schmeckt ihm auch nicht.
Macht auch keinerlei Sinn Parameter zu übergeben, die nicht verändert werden können.
Weil Sinnfrei, haut er einem auch dieses um die Ohren.


Eine mögliche Variante:

void steuerung ()
{
  constexpr byte pinT = 42;
  constexpr byte pinL = 44;
}

Natürlich geht auch per Template Parameter.

template<byte pinT = 42,byte pinL = 44>void steuerung ()
{
}


// Aufruf dann:
steuerung(); // default Parameter
steuerung<5,7>();// custom Parameter

Was auch geht, ist die ganze Funktion als constexpr zu definieren
wird automatisch erzwungen, dass die Parameter schon zu Compilezeit bekannt sein müssen.
Die Funktion wird zur Compilezeit ausgeführt, wenn die Parameter konstant/Literale sind.
Landet dann nicht im generierten Code.

constexpr int steuerung (int a, int b)
{
  return a+b;
}

1 Like

Nachtrag/Disclaimer:

Was sich die Entwickler dabei gedacht haben?
Da ich nicht an der Entwicklung von C++ beteiligt bin, kann ich solche Dinge nicht felsenfest belegen.

Sehe allerdings die Notwendigkeit, eine Sprache möglichst scharfkantig zu definieren.
Sehe auch die Paradoxie in constexpr Funktionsparametern.
Aus dem Grund halte ich das Verbot für vollkommen OK.

Was wäre wenn Gedankenspiele kann man betreiben, ist Gehirnakrobatik.
Bleibt aber sicherlich ohne Einfluss auf die Realität.

Es sei denn:
Beschwerden/Verbesserungen können an anderer Stelle eingereicht werden.

1 Like

Danke euch für die Antworten :slight_smile:

Ja das klingt logisch, allerdings sind sie doch in diesem Fall

Man könnte sie ja sogar ändern zu


constexpr byte pinTaster {2};
constexpr  byte pinLed {12};

Da könnte/würde sich doch nichts mehr ändern?

Und?
Was ist in dieser Situation?

constexpr byte pinTaster1 { 2};
constexpr byte pinLed1    {12};
constexpr byte pinTaster2 { 3};
constexpr byte pinLed2    {13};

steuerung(pinTaster1,pinTaster1);
steuerung(pinTaster2,pinTaster2);

Bedenke, dieses "was wäre wenn Spielchen" ist irrelevant.
Es ist verboten das zu tun was du willst. Es ist zudem Paradox/Widersprüchlich.
Damit ist der Sack zu.


Ich könnte dir Forth empfehlen.
Da mag sowas gehen, denn:
In Forth wird die Sprache solange modifiziert, bis sie das Problem optimal abbildet.
Forth Programmierer bauen zu dem Behufe häufiger mal ihre eigenen Compiler und Interpreter.
Mehrere Dutzend davon, stecken in so manchem Forth System.

Hallo,

das mit den Schlüsselwörtern ist schon etwas verrückt um das milde auszudrücken. Ich tue mich da auch manchmal schwer. constexpr garantiert eigentlich nur für Variablen die absolute Konstanz zur Compilezeit. Für constexpr Funktionen kann der Compiler immer noch entscheiden ob er diese doch nur zur Laufzeit ausführt. Das ist schon verwirrend.

Ab C++20 gibts es deswegen 2 neue Schlüsselwörter. consteval und constinit

Nochmal wegen const.

void foo (const byte a, const byte b)
{ ... }

Wie schon gesagt wurde. Das const sagt dem Compiler hierbei nur das die Variablen innerhalb der Funktion nicht verändert werden. Sie sind ja nur lokal sichtbar. Die Funktion kann dennoch mit unterschiedlichen Übergabewerten aufgerufen werden. Deswegen kann hier kein constexpr verwendet werden, weil die Übergabeparameter mit jedem Funktionsaufruf unterschiedlich sein können, also eben keine Konstanz zur Compilezeit vorhanden ist. Selbst wenn man Konstanten als Parameter übergibt. Die äußeren konstanten Parameter sind ja für die Funktionsaufrufe wiederum unterschiedlich. Man muss unterscheiden zwischen Parameterübergabe und was innerhalb der Funktion passiert.

Mal sacken lassen ...

Das sind alles Feinheiten für gezielte Optimierungen. Ich denke wenn jeder erstmal wenigstens für "konstante" Variablen wirklich const verwenden würde, dann wäre der Programmierwelt auch schon geholfen. const ist schon eine gute Hürde bzw. Hilfe um Tippfehler oder unabsichtliche Änderungen durch den Compiler feststellen zu lassen.

3 Likes

Danke euch :+1:t3:
Ich denke jetzt habe ich es :smiley:

Durchaus!
Aber leider noch nicht in der Arduinowelt, außer z.B. bei dir und mir.
Die Dichte ist noch gering, hier im Forum, würde ich mal sagen....

Auch hier gilt wieder, die Doku hat natürlich recht.

Mein Compiler wirft schon Errors oder warnt wenigstens, wenn man in einer constexpr Funktion Mist baut.

Beispiele:

error: call to non-'constexpr' function xxxxx
error: lvalue-to-rvalue conversion of a volatile lvalue

Hallo,

ist natürlich etwas Schade das die IDE soweit hinterher hinkt. Mir fällt es langsam schwer lieb gewonnene Annehmlichkeiten für Forum Beispiele auf "alt" umzuschreiben. Wenn da wenigstens der gcc 9.4 mit kompletten C++17 Standard wäre, dass wäre schon top. Der gcc 9 ist ja nun gut abgehangen vorm nächsten großen Umbruch. Die IDE 2.0 bringt übrigens den gleichen avr-gcc 7.3 mit wie die IDE 1.9.x Verstehe ich nicht ganz. Aber solange es keine 2.x Portable gibt ist die sowieso nichts für mich.

Hallo,

desweiteren habe ich mir gedacht, wenn der TO schon etwas lernen möchte, dann zeige man ihn den nächsten logischen Schritt um von den lokalen statischen Variablen wegzukommen. Das sind hier nur Einmalfunktionen. Ich weiß nicht aus welchen Thread der Sketch stammt und um was es dabei konkret ging. Vielleicht um pauschal enum mit switch case zu zeigen. Ist auch egal.

Deshalb etwas umgebaut. Die Heartbead Funktion habe ich fast so belassen nur eingeschränkt um eine Mehrfachverwendung zu erschweren.

Am Rest kann sich der TO austoben und auf class mit private/public/Methoden usw. umbauen wenn ihm weiterhin Spass macht. Das Bsp. soll unabhängige Taster-Led Objekte Instanzen zeigen. Jedes Objekt hat seinen eigenen Datensatz.

Sketch
/*  
  https://forum.arduino.cc/t/constexpr-als-argument-in-funktion-not-allowed/1090481
  Arduino Mega 2560
  16.02.2023
*/

struct Daten
{  
  enum state {EIN, PAUSE_EIN, AUS, PAUSE_AUS, NICHTS};
  
  const uint8_t pinTaster;
  const uint8_t pinLed;
  const uint32_t blinkpause;
  uint32_t lastMillisLed;
  uint32_t lastMillisTaster;
  bool stateTaster;
  state zustand;

  // Konstruktor
  Daten (uint8_t t, uint8_t l, uint32_t p) :
  // Initialisierungsliste
    pinTaster  {t},
    pinLed     {l},
    blinkpause {p},
    lastMillisLed {0},
    lastMillisTaster {0},
    stateTaster  {false},
    zustand {state::NICHTS}
  {}
  
  void init (void)
  {
    pinMode(pinTaster, INPUT_PULLUP);
    pinMode(pinLed, OUTPUT);
  }

  void steuerung (void)
  {
    switch (zustand)
    {
      case state::NICHTS:
        if (updateTaster() ) {
          zustand = state::EIN;
        }
        break;

      case state::EIN:
        digitalWrite(pinLed, HIGH);
        lastMillisLed = millis();
        zustand = state::PAUSE_EIN;
        break;

      case state::PAUSE_EIN:
        if ( millis() - lastMillisLed >= blinkpause) {
          zustand = state::AUS;
        }
        break;

      case state::AUS:
        digitalWrite(pinLed, LOW);
        lastMillisLed = millis();
        zustand = state::PAUSE_AUS;
        break;

      case state::PAUSE_AUS:
        if ( millis() - lastMillisLed >= blinkpause) {
          zustand = state::NICHTS;
        }
        break;
    }
  }

  bool updateTaster (void)
  {
    if (millis() - lastMillisTaster >= 30) {
      lastMillisTaster = millis();
      stateTaster = !(digitalRead(pinTaster));
    }
    return stateTaster;
  }
};

Daten control (2, 28, 250);
Daten control2 (3, 29, 500);

void setup (void)
{
  pinMode(LED_BUILTIN, OUTPUT);
  control.init();
  control2.init();
}

void loop (void)
{
  control.steuerung();
  control2.steuerung();
  ledBuiltinHeartBeat(1000);
}

void ledBuiltinHeartBeat (const uint16_t interval)  // Kontrolle ob Sketch blockiert
{
  static uint32_t lastMillis {0};
  static bool state {LOW};
  const uint32_t ms {millis()};

  if (ms - lastMillis >= interval) {
    lastMillis = ms;
    state = !state;
    digitalWrite(LED_BUILTIN, state);
  }
}
1 Like

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