[gelöst] Ellipsenfunktionen und der Stack der Funktion



  • Hallo zusammen,

    ich habe eine Funktion die als Parameter eine Ellipse bekommt. Diese Ellipse soll per Konvention unsigned 32 bit integer entgegennehmen.

    Diese ellipsen parameter sollen nun an eine Funktion übergeben werden die ein Array erwartet. Das Problem ist, dass auf unserer Zielplattform kein dynamisches Memorymanagement erlaubt ist. Ich hatte folgende Idee, die ich allerdings für einen sehr bösen Hack halte.

    void func(unsigned int items, ...)
    {
        unsigned int* p = (&items + 1);
        func1(p, items);
    };
    
    void func1(unsigned int* array, unsigned int size)
    {
        // ...
    }
    

    Mein Problem ist, dass ich die Schnittstellenfunktion mit der Ellipse leider nicht ändern kann.

    Meine Frage ist ob dieser Aufruf Undefined Behavior ist, oder ob der Aufbau des Stacks für die Übergabeparamter so bestehen bleibt.

    Ansonsten würde ich mich über Vorschläge, die das Problem besser lösen, freuen.

    Unser Projekt wird mit einem C++ Compilier kompiliert.

    Mfg
    Tobi

    PS.
    Wenn ich hier im falschen Forum bin, bitte verschieben. Ich wusste nicht wo ich besser aufgehoben bin.

    **
    Edit:
    Das Problem hat sich erledigt. Ich habe eine Interfaceänderung durchbekommen. Danke an alle die geholfen haben...

    Zur Info:
    Auch ein alloca oder der Pointer auf das erste Ellipsen Element hätten auf unserer Plattform funktioniert
    **


  • Mod

    Hmm, dynamische Strukturen ohne dynamischen Speicher benutzen zu dürfen. Da wird dir wohl nicht viel anderes übrig bleiben als das was du jetzt vor hast. Im Standard sind Implementierungsdetails wie diese nicht vorgeschrieben und Pfuscherei damit ist in der Regel undefiniertes Verhalten. Das bedeutet aber nicht, dass es nicht funktionieren kann! Ich würde es einfach mal ausprobieren, ob es bei deiner Implementierung funktioniert. Es würde mich sehr wundern, falls nicht.

    Falls du die Möglichkeit hast, einen C99-Compiler zu benutzen oder dein C++-Compiler C99-Erweiterungen besitzt: Da gibt es auch Arrays auf dem Stack mit variabler Länge.



  • Hallo SeppJ,

    danke für die Antwort. Ich habe schon fast befürchtet dass das hier undefiniertes Verhalten ist. Die Implementierung so wie sie ist funktioniert auf unserer Plattform.

    Eine C99 Erweiterung haben wir leider nicht. 😞

    Dann werde ich wohl mit dieser Lösung leben müssen.



  • Tobias Gerg schrieb:

    Ich hatte folgende Idee, die ich allerdings für einen sehr bösen Hack halte.
    [...]
    Meine Frage ist ob dieser Aufruf Undefined Behavior ist, oder ob der Aufbau des Stacks für die Übergabeparamter so bestehen bleibt.

    Du vermischst hier C++ ISO Standard und eine bestimmte Implementierung dessen. Der C++ ISO Standard sagt, dass man Variablen wie einelementige Arrays bzgl Zeiger behandeln kann. Also:

    int main()
    {
      int i = 23;
      int*p = &i;  // ok
      int*q = p+1; // ok ("eins hinter das letzte Element")
      int*r = p+2; // NICHT ok (U.B.)
      int*z = p-1; // NICHT ok (U.B.)
      int a = *p;  // ok
      int b = *q;  // NICHT ok (U.B.)
    }
    

    Der C++ ISO Standard definiert auch nicht näher, was va_list für ein Typ ist und wie die va_*-Makros definiert sein müssen. Er macht also keine Garantien, die für Deinen Fall notwendig wären.

    Wenn es eine Obergrenze von Zahl der Parametern gibt, könntest Du natürlich vorschriftsmaessig die Elemente in ein lokales Array übertragen, welches groß genug ist, und dann weiter arbeiten. Eventuell unterstützt Dein Compiler ja auch VLAs (variable-length-arrays). Dann bräuchtest Du keine Obergrenze selbst wählen und erstellst einfach ein lokales Array mit der richtigen Größe.

    Wenn es nur um die Aufruf-Kompatibilität geht -- also, dass "die Funktion" wie eine var-arg-Funktion aufrufbar sein soll und mehr nicht -- und wenn der Compiler variadische Templates unterstützt, könnte man auch die benutzen, um var-args typsicher zu simulieren und ein entsprechendes Array erzeugen.

    inline copy(unsigned arr[]) {}
    
    template<class...More>
    inline void copy(unsigned arr[],unsigned i, More...more)
    {
      arr[0] = i;
      copy(arr+1,more...);
    }
    
    template<class...Types>
    void func(int items, Types...more)
    {
      unsigned arr[sizeof...(Types)];
      copy(arr,more...);
      u.s.w.
    }
    

    Wenn Du Glück hast, enthält die Doku Deines Compiler eine Beschreibung, wie var-args implementiert werden, und dass man sich auf diese Implementierung ab Version x.y des Compilers verlassen kann. Im Notfall guckst Du mal nach, wie die va_*-Makros aus dem <cstdarg>-Header expandiert werden und was va_list für ein Typ ist. Das hat Dich zwar normalierweise nicht zu interessieren, aber in diesem Fall wäre es ja extrem praktisch zu wissen, wie das implementiert wurde. Das blöde bei dem Ansatz ist nur, dass solche Implementierungsdetails sich von Version zu Version des Compilers ändern können. Ich würde also zumindest per #if überprüfen, ob es sich auch um den richtigen Compiler in der richtigen Version handelt und andernfalls vielleicht einen Fehler (#error) oder zumindest eine Warnung ausgeben lassen.



  • Hallo krümelkacker,

    danke für deine Ausführungen. Leider werden variadische Templates werden bei unserem Compiler leider noch nicht unterstützt. Von daher werde ich mich mal schlau machen wie die va_* Makros expandiert werden.

    Danke auch für den #ifdef Hinweis. Dass werde ich so noch einbauen damit man zumindest eine kleine Sicherheit hat. Vielleicht kann ich mit deinen und SeppJ's Argumenten auch eine Interfaceänderung durchdrücken...

    Greets
    Tobi



  • Du könntest die Elemente auch umkopieren, dann wäre es standardkonform.
    Natürlich kostet das etwas Performance, aber das ist oft verschmerzbar. Kommt halt drauf an wie oft die Funktion aufgerufen wird, und wie rechenaufwendig die Funktion selbst ist.


  • Mod

    hustbaer schrieb:

    Du könntest die Elemente auch umkopieren, dann wäre es standardkonform.

    Aber wie? Das ist doch genau das was hier gesucht ist.



  • SeppJ schrieb:

    hustbaer schrieb:

    Du könntest die Elemente auch umkopieren, dann wäre es standardkonform.

    Aber wie? Das ist doch genau das was hier gesucht ist.

    Das war doch schon einer meiner Vorschläge: lokales Array anlegen, was genügend groß ist...

    const int max_items = 100;
    
    void func(int items, ...)
    {
      assert(items<=max_items);
      unsigned array[max_items];
      va_list vl;
      va_start(vl,items);
      for (int i=0; i<items; ++i) {
        array[i] = va_arg(vl,unsigned);
      }
      va_end(vl);
    
      ... auf array arbeiten ...
    }
    

    ggf auch mit VLAs, falls der Compiler das unterstützt.

    is aber schon irgendwie blöd, dass die Werte in ein Array kopiert werden, wenn sie eh wahrscheinlich schon alle hintereinander im Speicher stehen (siehe va_*-Implementierung).

    kk



  • SeppJ schrieb:

    hustbaer schrieb:

    Du könntest die Elemente auch umkopieren, dann wäre es standardkonform.

    Aber wie? Das ist doch genau das was hier gesucht ist.

    Ich dachte mir dass das "wie" trivial ist, und keiner weiteren Erklärung bedarf. Falls nicht hat es krümelkacker ja jetzt schon gezeigt.
    Was noch fehlt wäre ein alternativer Pfad, der z.B. std::vector verwendet wenn items > max_items .

    ----

    krümelkacker schrieb:

    is aber schon irgendwie blöd, dass die Werte in ein Array kopiert werden, wenn sie eh wahrscheinlich schon alle hintereinander im Speicher stehen (siehe va_*-Implementierung).

    Ja, is doof irgendwie.
    Genau so doof, wie dass man nicht rausbekommt wie viele Bytes überhaupt übergeben wurden.
    Andrerseits ist durch Templates etc. die Motivation vermutlich recht klein, die "..." Geschichte in C++ noch zu verbessern.



  • Hallo hustbaer,
    danke für die Antwort.

    hustbaer schrieb:

    ...
    Ich dachte mir dass das "wie" trivial ist, und keiner weiteren Erklärung bedarf. Falls nicht hat es krümelkacker ja jetzt schon gezeigt.
    Was noch fehlt wäre ein alternativer Pfad, der z.B. std::vector verwendet wenn items > max_items .
    ---

    Leider kenne ich die Anzahl der max_items nicht. Und ich kann auch keinen std::vector nehmen da bei uns keine stl Implementierung benutzt werden darf...

    hustbaer schrieb:

    krümelkacker schrieb:

    is aber schon irgendwie blöd, dass die Werte in ein Array kopiert werden, wenn sie eh wahrscheinlich schon alle hintereinander im Speicher stehen (siehe va_*-Implementierung).

    Ja, is doof irgendwie.
    Genau so doof, wie dass man nicht rausbekommt wie viele Bytes überhaupt übergeben wurden.
    Andrerseits ist durch Templates etc. die Motivation vermutlich recht klein, die "..." Geschichte in C++ noch zu verbessern.

    ACK



  • Tobias Gerg schrieb:

    Leider kenne ich die Anzahl der max_items nicht. Und ich kann auch keinen std::vector nehmen da bei uns keine stl Implementierung benutzt werden darf...

    Naja, dann kannst du zur Not malloc+free verwenden.
    (Bzw. new[] + delete[])



  • Würde ich gerne, aber...

    Tobias Gerg schrieb:

    ...
    Das Problem ist, dass auf unserer Zielplattform kein dynamisches Memorymanagement erlaubt ist.
    ...

    Leider 😞



  • Oops, sorry, übersehen.
    Dann sind wirklich nur mehr VLAs möglich...

    EDIT: oder könntest du die Funktion die ein Array erwartet vielleicht so anpassen dass sie statt dessen eine va_list + einen size_t als Parameter nimmt?



  • Ich bin ja so doof 😃

    Nimm einfach alloca(). Natürlich solltest du vorher gucken ob in der Doku zu deiner Plattform irgendwelche Probleme/Fallstricke erwähnt sind. Normalerweise funktioniert alloca supi, aber bei einigen Plattformen soll es problematisch sein.



  • hustbaer schrieb:

    Ich bin ja so doof 😃

    Nimm einfach alloca(). Natürlich solltest du vorher gucken ob in der Doku zu deiner Plattform irgendwelche Probleme/Fallstricke erwähnt sind. Normalerweise funktioniert alloca supi, aber bei einigen Plattformen soll es problematisch sein.

    👍 Das ist ja mal der Hammer... 😮 Herzlichen dank für den Tipp, da werd ich gleich nochmal das Manual wälzen...


Anmelden zum Antworten