"array" aus Funktionen



  • Jetzt muss man aber anfangen zu fragen, wie du es verwenden willst.

    Hier ist eine Möglichkeit. Da ich nicht genau weiß wie du es einsetzen willst, kann ich es nicht konkreter machen.
    Vielleicht willst du ja auch nur eine eigene Variante vom Strategy Pattern bauen.

    class Bla // note: Implementierungen gegebenenfalls in cpp verschieben, aus der Klassendefinition heraus.
    {
    public:
        Bla()
            : functions { // initialisiere array
                Bla::berechnung1, // note: & ist optional
                Bla::berechnung2
            }
        {
        }
    
    private:
        // wenn sie nicht static sein sollen, bind oder lambdas benutzen.
        static /*inline*/ double berechnung1 (double x, int a, int b)
        {
            return x * (a + b);
        }
    
        static /*inline*/ double berechnung2 (double x, int a, int b)
        {
            return (a + b) / x;
        }
    
    public:
        // hierfür werde ich vermutlich gleich gesteinigt... 3... 2... 1...
        auto call(int index) -> decltype(berechnung1({}, {}, {})) {
            return functions[index] (2., 5, 8);
        }
    
    private:
        // hier sind die Alternativen aus dem Thread auch anwendbar.
        decltype(&berechnung1) functions[2];
    };
    
    int main()
    {
        Bla bla;
        std::cout << bla.call(1) << "\n";
    }
    


  • Das Interesse an der Klasse ist wirklich nur interessehalber. Ich habe eine Funktion (Methode, bin noch nicht sicher wann man Funktion oder Methode schreibt), die bis auch eine innere Berechnung (die natürlich etwas umfangreicher ausfällt, als obiges Beispiel) immer gleich aussieht. Um nicht mehrmals diese Methode auszuschreiben und um leichteren Zugriff bei Änderungen zu bekommen, habe ich nun die verschiedenen Berechnungen als eigenständige Funktionen ausgelagert. Sie werden dann innerhalb der Methode über einen Index aufgerufen. Dies konnte ich mit Euren obigen struct-Vorschlägen realisieren. So weit ist also alles in Ordnung, ich habe jetzt eine Vorstellung davon, wie man das machen kann.

    Nun dachte ich, das eine Klasse eventuell noch flexibler wäre und wollte einfach mal wissen, wie so eine ausschauen kann, weil ich mich damit noch nicht ernsthaft auseinandergesetzt habe. Deshalb danke erstmal für das Beispiel 😉


  • Mod

    lemon03 schrieb:

    Sie werden dann innerhalb der Methode über einen Index aufgerufen.

    Warum?



  • Das hatte ich mich natürlich vorher auch gefragt und ich bin auf keine andere Lösung gekommen. Wenn man, sagen wir 5 verschiedene Arten von Berechnungen hat, die in einem Methodenkörper ausgeführt werden, der Körper selbst aber immer gleich aussieht, macht es doch Sinn, den Körper nur einmal auszuschreiben und nur die Berechnungen aufzurufen? Und mir ist nun mal nichts anderes eingefallen, als diese über einen Index aufzurufen.



  • Vielleicht findest du eine map intuitiver.

    std::unordered_map <std::string, std::function <int(int, int)> > map = {
        {"max", &max},
        {"min", &min},
        {"add", &add}
    };
    
    map["max"](2, 3);
    


  • Ist dies an die Frage von SeppJ gerichtet?

    Denn ich fand Deinen ersten Vorschlag in diesem Thread genau passend für meine Bedürfnisse. Die Frage nach dem "Warum?" habe ich versucht zu beantworten, weil sie wohl eine Sinnlosigkeit meines Tuns implizierte und wollte dies erklären in der Erwartung, diese Sinnlosigkeit auch gezeigt zu bekommen.

    Die Frage, ob man diese struct auch als Klasse schreiben kann, war wirklich nur aus Interesse an Klassen-Srukturen. Nicht mehr und nicht weniger.


  • Mod

    lemon03 schrieb:

    Denn ich fand Deinen ersten Vorschlag in diesem Thread genau passend für meine Bedürfnisse. Die Frage nach dem "Warum?" habe ich versucht zu beantworten, weil sie wohl eine Sinnlosigkeit meines Tuns implizierte und wollte dies erklären in der Erwartung, diese Sinnlosigkeit auch gezeigt zu bekommen.

    Das ist auch durchaus immer noch mein Eindruck, dass das ziemlich sinnlos klingt. Aber es war mir zu mühsam, dir alle Details aus der Nase zu ziehen, wenn du nicht selber damit herausrücken möchtest.



  • Dann muss ich höflichst zurück fragen, was an meiner Antwort gefehlt hat?

    Denn ich kann nicht Dinge beantworten, von denen ich eventuell noch keine Kenntnisse habe. Du wirst sie gewiss haben, weswegen man solche Frage-Antworten Spielchen deutlich schneller vorantreiben könnte, wenn Du mir einfach schreibst, weshalb Du bei meiner Vorgehensweise nach einem "warum?" fragst.


  • Mod

    Dann zeig doch mal, wie es jetzt ist; erklär, was du mit dem jetzigen Zustand erreichen möchtest; dann zeig wie du es dir vorstellst; und warum das, was du dir vorstellst, eine Verbesserung wäre.

    Denn ich kann nicht Dinge beantworten, von denen ich eventuell noch keine Kenntnisse habe. Du wirst sie gewiss haben, weswegen man solche Frage-Antworten Spielchen deutlich schneller vorantreiben könnte, wenn Du mir einfach schreibst, weshalb Du bei meiner Vorgehensweise nach einem "warum?" fragst.

    Du könntest einfach ein bisschen freigiebiger mit Informationen sein. Ist doch selbstverständlich, dass wir dir besser helfen können, wenn du uns dein Problem genauer erklärst. Stattdessen philosophierst du über den Sinn von Fragen und den Inhalt von Antworten herum. Beispielsweise wusstest du doch genau, dass ich mir mehr Informationen gewünscht hätte, aber dein letzter Beitrag hat absolut 0 Bezug zum Problem und hat damit effektiv mehr als eine Stunde Zeit verschwendet, in der ich dir vielleicht hätte helfen können, anstatt diese Antwort zu schreiben, die ebensowenig mit dem eigentlichen Problem zu tun hat.



  • Ok. Ich bitte um Verständnis, wenn ich keine Lust habe, noch einen Beispielcode zu schreiben, deshalb zweige ich gleich das Einsatzgebiet. Allerdings zeigt dieser auch nur das, was ich meine vorhin beschrieben zu haben. Kann aber auch tatsächlich ein Irrtum meinerseits sein.

    //die diversen Berechungsfunktionen
    int calc_plasma_1 (intptr_t col_size, unsigned int x, unsigned int y, double, double)
    {
        int color = int
                    (
                        (col_size/2) + ((col_size/2) * sin(x / 8.0))
                        + (col_size/2) + ((col_size/2) * sin(y / 6.0))
                    ) / 2;
        return color;
    }
    
    int calc_plasma_2 (intptr_t col_size, unsigned int x, unsigned int y, double, double)
    {
        int color = int
                    (
                        col_size/2 + (col_size/2 * sin(x / 16.0))
                        + col_size/2 + (col_size/2 * sin(y / 8.0))
                        + col_size/2 + (col_size/2 * sin((x + y) / 16.0))
                        + col_size/2 + (col_size/2 * sin(sqrt(double(x * x + y * y)) / 6.0))
                    ) / 4;
        return color;
    }
    
    int calc_plasma_3 (intptr_t col_size, unsigned int x, unsigned int y, double, double)
    {
        int color = int
                    (
                        col_size / 2 + (col_size / 2 * sin(x / 10.0))
                        + col_size / 2 + (col_size / 2 * sin(y / 16.0))
                        + col_size / 2 + (col_size / 2 * sin(sqrt(double((x - Width / 2.0) * (x - Width / 2.0) + (y - Height / 2.0) * (y - Height / 2.0))) / 6.0))
                        + col_size / 2 + (col_size / 2 * sin(sqrt(double(x * x + y * y)) / 6.0))
                    ) / 4;
        return color;
    }
    
    const std::function <int (intptr_t, unsigned int, unsigned int, double, double)> Plasma::calc_plasma[4] =
    {
        &calc_plasma_1,
        &calc_plasma_2,
        &calc_plasma_3,
        &calc_plasma_4
    };
    
    //mein gemeinter "Methodenkörper"
    void showPlasmaScroller(HANDLE set,
                            std::vector <CHAR_INFO> palette,
                            std::vector <CHAR_INFO> palette_Chars,
                            std::vector <Char> charSet,
                            Plasma callPlasma,
                            size_t plasma_idx,
                            std::string text, double delay_col)
    {
        std::array <std::array <unsigned int, Height>, Width> plasma, buffer;
        std::array <CHAR_INFO, Width * Height> outBuffer;
        intptr_t col_size = (intptr_t)palette.size();
        size_t color;
        /*generate colors*/
        for(unsigned int x=0; x<Width; x++)
        {
            for(unsigned int y=0; y<Height; y++)
            {
                //Aufruf der diversen Berechnungsfunktionen
                color = callPlasma.calc_plasma[plasma_idx](col_size, x, y, 0.0, 0.0);
                plasma[x][y] = color;
            }
        }
        /*cycle colors & charScroll*/
      [EDIT: ab hier unerheblich fürs Thema]
    }
    

    (Die beiden doubles und die vierte Funktion gehören zu einer weiteren etwas komplexeren Methode, die ich noch mal extra ausschreiben musste, wollte die Funktion aber trotzdem schon dabei haben)

    Ich kann so unterschiedliche PlasmaScroller aufrufen, nur über die Variable plasma_idx.
    Der Punkt aber ist: Ich habe keine Ahnung, wie ich obige diverse Berechnungsfunktionen ohne diesen Index aufrufen kann.


  • Mod

    Gibt es einen tieferen Grund, wieso nicht einfach direkt die gewünschte Funktion an showPlasmaScroller übergeben wird?



  • Mmh ... ich schätze, weil ich im Moment auf dem Schlauch stehe. Kann sein, das es mir morgen oder später noch einfällt, im Moment habe ich aber keine Vorstellung, wie ich eine Funktion einer Funktion übergebe und wie ich sie dort aufrufen kann?


  • Mod

    lemon03 schrieb:

    Mmh ... ich schätze, weil ich im Moment auf dem Schlauch stehe. Kann sein, das es mir morgen oder später noch einfällt, im Moment habe ich aber keine Vorstellung, wie ich eine Funktion einer Funktion übergebe und wie ich sie dort aufrufen kann?

    Mit Funktionszeigern? Also dem Mittel, um das sich dieser Thread dreht?

    Besser wäre natürlich ein Template oder eventuell std::function, damit man flexibler bleibt in dem, was man als Funktion übergeben kann.



  • Da sich der Aufruf hier in einer relativ engen Schleife befindet hätte std::function möglicherweise einen merkbaren Overhead.
    Wenn nix dagegen spricht würde ich also eher nen Funktionszeiger nehmen oder aus der Funktion ein Template machen.

    Wobei... die Funktionen machen ja doch einiges. Da wird ja viel gerechnet u.a. mit Winkelfunktionen. Wird also vermutlich doch keinen grossen Unterschied machen.

    @lemon03
    Statt dem Array mit Datentyp T[4] macht du nen Funktionsparemter mit Datentyp T. Und rufst den dann einfach wie ne Funktion auf. Wie du es jetzt schon mit callPlasma.calc_plasma[plasma_idx] machst.



  • Ist zwar OT, trotzdem möchte ich auf std::hypot hinweisen. Eine Funktion, die leider ein Schattendasein fristet... 😞



  • Gut ok, danke. Muss mich noch ein wenig einlesen in die Vorschläge 😉



  • Habe es mal mit einem Funktionszeiger versucht.

    int formel_1(int a, int b){
    
        return a*b;
    }
    
    int formel_2(int a, int b){
    
        return a/b;
    }
    
    void calc_(int (*formel)(int, int))
    {
        int a = 40, b = 8;
        int ergebnis = formel (a, b);
        std::cout << ergebnis << '\n';
    }
    
    void aufruf_(int (*formel)(int, int))
    {
        formel = &formel_1;
        formel = &formel_2;
    
        calc_(formel_1);
        calc_(formel_2);
    
    }
    
    int main()
    {
    
        int (*formel)(int, int) = 0; // Anlegen eines Funktionszeigers, Initialisierung mit 0
    
        aufruf_(formel);
    
    }
    

    Wäre der soweit in Ordnung oder kann man das besser formulieren?



  • Was soll jetzt die aufruf_ Funktion und der Funktionspointer dort als Parameter? Ansonsten sieht das dann schon OK aus. Angewandt auf deinen Code würde deine showPlasmaScroller Funktion statt dem size_t plasma_idx Parameter dann einfach einen Funktionspointer nehmen und diese Funktion aufrufen.



  • Ich wollte das Beispiel eben näher bei meinem aktuellen Einsatzzweck schreiben. Und calc_() soll nicht in der main aufgerufen werden, sondern in einer weiteren Funktion.

    Mit Funktionspointer kann ich jetzt aber nichts anfangen. Ist ein Zeiger nicht ein Pointer bzw?



  • Ja Zeiger = Pointer. Jedenfalls ist der Parameter in der aufruf_ Funktion ziemlich sinnlos. Du übergibst von der main Funktion einen Zeiger der aber auf 0 zeigt, dann schreibst nacheinander die Adressen von zwei Funktionen da rein, nutzt den Pointer aber nie. So würde die Funktion genau das gleiche tun:

    void aufruf_()
    {
        calc_(formel_1);
        calc_(formel_2);
    }
    

Anmelden zum Antworten