"array" aus Funktionen



  • Hallo,

    ich habe wieder eine Vollblockade. Ich benötige ein "array" aus Funktionen, wie immer so eine Konstruktion aussieht. Da ich dazu keine Quellen gefunden habe und ich auch keine Vorstellung dazu habe, wollte ich meine mir bekannte array aus struct wählen. Dort komme ich nicht weiter. Vielleicht muss ich sogar mit Klassen anfangen, aber auch meine ersten Ausflüge dorthin helfen nicht weiter. Immer Bäume, Zelte, Autos, da blick ich noch nicht richtig durch.

    struct berechnung
    {
    
        static double formel (double, int, int);
    };
    
    double berechnung::formel (double x, int a, int b)
    {
        return x * (a + b);
    }
    
    double berechnung::formel[?] (double x, int a, int b)
    {
        return (a + b) / x;
    }
    
    int main()
    {
    
        double ausgabe = berechnung::formel[?] (0.5, 2, 2);
        std::cout << ausgabe;
    
    }
    

    Wie bekomme ich es hin, mit einem Index auf eine bestimmte Formel zuzugreifen?



  • #include <iostream>
    #include <functional>
    
    double berechnung1 (double x, int a, int b)
    {
        return x * (a + b);
    }
    
    double berechnung2 (double x, int a, int b)
    {
        return (a + b) / x;
    }
    
    struct Bla
    {
        static const std::function <double(double, int, int)> functions[2];
    };
    
    const std::function <double(double, int, int)> Bla::functions[2] = {
        &berechnung1,
        &berechnung2
    };
    
    int main()
    {
        Bla bla;
        std::cout << bla.functions[0](2., 2, 3) << "\n";
        std::cout << bla.functions[1](2., 2, 3) << "\n";
    }
    

    So in etwa?


  • Mod

    Und warum function ?



  • Arcoth schrieb:

    Und warum function ?

    Meinst du warum nicht plain Funktionszeiger?

    struct Bla
    {
        static const std::array <double(*)(double, int, int), 2> functions;
    };
    
    const std::array <double(*)(double, int, int), 2> Bla::functions = {
        &berechnung1,
        &berechnung2
    };
    

    Weil ich die Schreibweise von Funktionszeigern eklig finde.

    Bei so kleinen Funktionen kann man es auch gleich so machen.

    struct Bla
    {
        static const std::function <double(double, int, int)> functions[2];
    };
    
    const std::function <double(double, int, int)> Bla::functions[2] = {
        [](double x, int a, int b) {
            return x * (a + b);
        },
        [](double x, int a, int b) {
            return (a + b) / x;
        }
    };
    

    EDIT: Oder die Funktionen als (private)(static) member der Klasse. Darüber kann man streiten, und kommt drauf an ob man sie sonst noch anderweitig verwenden will.



  • Sieht gut aus, danke 🙂 Werde mich dann nochmal melden.



  • 5cript schrieb:

    Weil ich die Schreibweise von Funktionszeigern eklig finde.

    Ich finde die Implementierung von std::function eklig.

    #include <iostream>
    #include <functional>
    
    double berechnung1 (double x, int a, int b)
    {
        return x * (a + b);
    }
    
    double berechnung2 (double x, int a, int b)
    {
        return (a + b) / x;
    }
    
    typedef double rechFuncT(double,int,int);
    
    struct Bla
    {
        static const rechFuncT* functions[2];
    };
    
    const rechFuncT* Bla::functions[2] = {
        &berechnung1,
        &berechnung2
    };
    
    int main()
    {
        Bla bla;
        std::cout << bla.functions[0](2., 2, 3) << "\n";
        std::cout << bla.functions[1](2., 2, 3) << "\n";
    }
    

  • Mod

    volkard schrieb:

    5cript schrieb:

    Weil ich die Schreibweise von Funktionszeigern eklig finde.

    Ich finde die Implementierung von std::function eklig.

    Ich finde gute Implementierungen von function clever, aber hier unnötig allgemein.

    Das Typedef darfst du übrigens auf einen Zeiger ausweiten, in etwa so:

    using rechFuncT = decltype(&berechnung1);
    


  • Mmh ... ich wollte nur kurz schreiben, das ich die Vorschläge (so ich es verstehe, liegen die Unterschiede nur im Stil?) recht gut bei mir verwenden konnte. Danke nochmal 😉



  • Hätte jemand Lust, mir obiges Beispiel als Klasse darzustellen? Ich habe zwar gelesen, das structs auch nur Klassen sind (?), wo aber der Standard alles public ist.
    Würde aber zu gerne sehen, wie in diesem Fall eine Klasse ausgeschrieben aussieht.



  • 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?


Anmelden zum Antworten