Wie am besten Java in C++ Programm umwandeln?



  • @Lennox Macht das Programm denn in Java was du erwartest? Ich sehe auf Anhieb nämlich nicht, warum dein ungültiges Beispiel von deiner main verworfen werden sollte. Tipp: In kleinere Teile zerlegen und Unit Tests schreiben

    Mir ist nicht klar, warum deine State Machine brauchst? Es gibt verschiedene Dinge, die man damit lösen kann, aber um Kommandozeilenparameter zu parsen und überprüfen würde ich die jetzt nicht nehmen. Mir wäre da auch nicht klar, was die Zustände und die Zustandsübergänge sind.

    Die statevariable wird auch nur gesetzt und nirgends verwendet. D.h. du könntest den ganzen fsm Kram löschen und die Funktion würde exakt dasselbe machen.

    Wenn ich dich richtig verstehe, kann man ein Target angeben und man kann einen Sleep angeben. Muss man aber nicht, wenn nicht gibt es einen default.

    Dann gibt es verschiedene Aktionen, die einen Parameter benötigen, (eine beliebige Nummer).

    Was irgendwie eine triviale Möglichkeit wäre, wäre sowas:

    #include <vector>
    #include <algorithm>
    #include <iostream>
    #include <string>
    
    enum class Action
    {
        A,
        B
    };
    
    void action1(std::string target, int action)
    {
    
    }
    
    void action2(std::string target, int action, int magic)
    {
    
    }
    
    void action3(int sleep)
    {
    
    }
    
    
    int main(int argc, char* argv[]) 
    {
        int i = 1; //first argument is program name
        std::vector<std::pair<Action, int>> actions;
       //default values
        std::string target="127.0.01";
        int sleep = 1000;
       //parse command line
        while (i < argc) 
        {
          std::string arg{argv[i]};
          size_t equals = arg.find('=');
          if (equals != std::string::npos) 
          {
            std::string key = arg.substr(0, equals);
            std::string value = arg.substr(equals + 1);
         
            if(key == "target1")
            {
                target=value;
            }
            if(key == "sleep")
            {
                sleep = std::stoi(value);
            }
            if(key == "a")
            {
                actions.emplace_back(std::pair(Action::A, std::stoi(value) ));
            }
            if(key == "b")
            {
                actions.emplace_back(std::pair(Action::B, std::stoi(value) ));
            }
         }
        }	
    //Perfom actions
        for(auto& action : actions)
        {
            switch (action.first)
            {
              case Action::A:
                {
                  action1(target, action.second);
                  action3(sleep);
                  break;
                }
               case Action::B:
                {
                  action2(target, action.second, 1000);
                  action3(sleep);
                  break;
                }
             default:
             break;
            }
        }
    
      return 0;
    }
    


  • @Schlangenmensch sagte in Wie am besten Java in C++ Programm umwandeln?:

    Macht das Programm denn in Java was du erwartest?

    Ja.

    Ich habe noch vergessen, eine doppelte Angabe von sleep oder target soll NICHT möglich sein. Das geht nicht ohne FSM.

    @Schlangenmensch sagte in Wie am besten Java in C++ Programm umwandeln?:

    Mir wäre da auch nicht klar, was die Zustände und die Zustandsübergänge sind.

    Zustand: Bei welcher Eingabe man gerade ist. Übergang: vom aktuellen Zustand durch die nächste Eingabe zum folgenden Zustand.

    0 = akzeptiere alles
    1 = sleep wurde angegeben, es darf nur noch target oder die Liste angegeben werden
    2 = target wurde angegeben, es darf nur noch sleep oder die Liste angegeben werden
    3 = nur noch die Liste darf angegeben werden, gleichzeitig Endzustand

    @Schlangenmensch sagte in Wie am besten Java in C++ Programm umwandeln?:

    Die statevariable wird auch nur gesetzt und nirgends verwendet. D.h. du könntest den ganzen fsm Kram löschen und die Funktion würde exakt dasselbe machen.

    Das stimmt nicht, schaue noch mal genau hin. Wenn fsm.get(state).containsKey(Symbol...) NICHT zutrifft, wird die Anwendung sofort beendet.



  • @Lennox sagte in Wie am besten Java in C++ Programm umwandeln?:

    Ich habe noch vergessen, eine doppelte Angabe von sleep oder target soll NICHT möglich sein. Das geht nicht ohne FSM.

    Oder mit jeweils einem bool Schalter.

    @Lennox sagte in Wie am besten Java in C++ Programm umwandeln?:

    fsm.get(state).containsKey(Symbol...)

    Ah... deine erlaubten Zustandsübergänge... Kommentare wäre praktisch.

    Mir scheint dein Ansatz overengineered zu sein, aber jeder so wie er mag.



  • @Schlangenmensch sagte in Wie am besten Java in C++ Programm umwandeln?:

    overengineered

    Na ja, Ansichtssache... Hierbei mit booleschen Variablen zu hantieren, das würde irgendwann unübersichtlich werden.

    Eigentlich wollte ich nur wissen, wie
    Map<Integer, Map<Symbol, Integer>> fsm = new HashMap<>(); in C++ geht, sowie wie
    List<Runnable> runnables = new LinkedList<>(); und runnables.forEach(Runnable::run); dann geht...

    Anstatt <Runnable> dann Funktionen? Und wie ist das mit den konkreten Parametern der Funktionen?

    In Java kann ich einfach runnables.add(() -> action1(finalTarget, finalNum)); schreiben, wenn die Parameter effektiv final sind. Vermutlich geht das in C++ aber nicht...



  • @Lennox
    std::map<int, std::map<Symbol, int>> fmt;

    für deine runnables musst du ein bisschen schauen, da du nur Funktionspointer mit der selben Signatur in einen Container bekommst. Aber std::function und std::bind sind deine Freunde.



  • @Schlangenmensch So?

    #include <map>
    #include <vector>
    #include <algorithm>
    #include <iostream>
    #include <string>
    
    enum class Symbol
    {
        NA,
        SLEEP,
        TARGET,
        ACTION1,
        ACTION2
    };
    
    enum class Action
    {
        A,
        B,
        C
    };
    
    typedef struct action_args
    {
        std::string target;
        int val;
        int magic;
        int sleep;
    } ACTION_ARGS;
    
    void action1(std::string target, int val)
    {
        std::cout << "action1: " << target << ", " << val << std::endl;
    }
    
    void action2(std::string target, int val, int magic)
    {
        std::cout << "action2: " << target << ", " << val << ", " << magic << std::endl;
    }
    
    void action3(int sleep)
    {
        std::cout << "action3: " << sleep << std::endl;
    }
    
    void exitApp(std::string cause)
    {
        std::cout << "WRONG: " << cause << std::endl;
        std::exit(0);
    }
    
    int main(int argc, char *argv[])
    {
        // default values
        std::string target = "0.0.0.0";
        int sleep = 1000;
    
        std::map<int, std::map<Symbol, int>> fsm;
        fsm[0][Symbol::SLEEP] = 1;
        fsm[0][Symbol::TARGET] = 2;
        fsm[0][Symbol::ACTION1] = 3;
        fsm[0][Symbol::ACTION2] = 3;
    
        fsm[1][Symbol::TARGET] = 3;
        fsm[1][Symbol::ACTION1] = 3;
        fsm[1][Symbol::ACTION2] = 3;
    
        fsm[2][Symbol::SLEEP] = 3;
        fsm[2][Symbol::ACTION1] = 3;
        fsm[2][Symbol::ACTION2] = 3;
    
        fsm[3][Symbol::ACTION1] = 3;
        fsm[3][Symbol::ACTION2] = 3;
    
        std::vector<std::pair<Action, ACTION_ARGS>> actions;
    
        // parse command line
        int i = 1;
        int state = 0;
        while (i < argc)
        {
            std::string arg{argv[i++]};
            size_t equals = arg.find('=');
            if (equals != std::string::npos)
            {
                std::string key = arg.substr(0, equals);
                std::string value = arg.substr(equals + 1);
                Symbol symbol = Symbol::NA;
    
                if (key == "sleep")
                {
                    symbol = Symbol::SLEEP;
                }
                if (key == "target")
                {
                    symbol = Symbol::TARGET;
                }
                if (key == "a")
                {
                    symbol = Symbol::ACTION1;
                }
                if (key == "b")
                {
                    symbol = Symbol::ACTION2;
                }
    
                if (symbol == Symbol::NA)
                {
                    exitApp("Unkown arg");
                }
                if (!fsm[state].count(symbol))
                {
                    exitApp("Not allowed arg");
                }
    
                if (symbol == Symbol::SLEEP)
                {
                    sleep = std::stoi(value);
                }
                if (symbol == Symbol::TARGET)
                {
                    target = value;
                }
                if (symbol == Symbol::ACTION1)
                {
                    actions.emplace_back(std::pair(Action::A, ACTION_ARGS{target, std::stoi(value), -1, sleep}));
                    actions.emplace_back(std::pair(Action::C, ACTION_ARGS{"", -1, -1, sleep}));
                }
                if (symbol == Symbol::ACTION2)
                {
                    actions.emplace_back(std::pair(Action::B, ACTION_ARGS{target, std::stoi(value), 1000, sleep}));
                    actions.emplace_back(std::pair(Action::C, ACTION_ARGS{"", -1, -1, sleep}));
                }
    
                state = fsm[state][symbol];
            }
        }
        if (actions.empty())
        {
            exitApp("No args ...");
        }
        actions.pop_back(); // Pop last unneeded action
    
        // Perfom actions
        for (const auto &action : actions)
        {
            const auto &a = action.second;
            switch (action.first)
            {
            case Action::A:
            {
                action1(a.target, a.val);
                break;
            }
            case Action::B:
            {
                action2(a.target, a.val, a.magic);
                break;
            }
            case Action::C:
            {
                action3(a.sleep);
                break;
            }
            default:
                exitApp("Should not happen");
            }
        }
    
        return 0;
    }
    

    Ungetestet. Und es ist länger geworden... Das muss doch irgendwie eleganter und robuster gehen?

    $ ./a.out target=localhost sleep=2500 a=123 a=23 b=34 a=1
    action1: localhost, 123
    action3: 2500
    action1: localhost, 23
    action3: 2500
    action2: localhost, 34, 1000
    action3: 2500
    action1: localhost, 1
    

    Was ist, wenn stoi einen schlechten Tag hat? Und... das ACTION_ARGS ist doch unschön.



  • Es geht auch ohne das störende typedef struct action_args 👏 ...

    #include <map>
    #include <vector>
    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <functional>
    
    enum class Symbol
    {
        NA,
        SLEEP,
        TARGET,
        ACTION1,
        ACTION2
    };
    
    enum class Action
    {
        A,
        B,
        C
    };
    
    void action1(std::string target, int val)
    {
        std::cout << "action1: " << target << ", " << val << std::endl;
    }
    
    void action2(std::string target, int val, int magic)
    {
        std::cout << "action2: " << target << ", " << val << ", " << magic << std::endl;
    }
    
    void action3(int sleep)
    {
        std::cout << "action3: " << sleep << std::endl;
    }
    
    void exitApp(std::string cause)
    {
        std::cout << "WRONG: " << cause << std::endl;
        std::exit(0);
    }
    
    int main(int argc, char *argv[])
    {
        // default values
        std::string target = "0.0.0.0";
        int sleep = 1000;
    
        std::map<int, std::map<Symbol, int>> fsm;
        fsm[0][Symbol::SLEEP] = 1;
        fsm[0][Symbol::TARGET] = 2;
        fsm[0][Symbol::ACTION1] = 3;
        fsm[0][Symbol::ACTION2] = 3;
    
        fsm[1][Symbol::TARGET] = 3;
        fsm[1][Symbol::ACTION1] = 3;
        fsm[1][Symbol::ACTION2] = 3;
    
        fsm[2][Symbol::SLEEP] = 3;
        fsm[2][Symbol::ACTION1] = 3;
        fsm[2][Symbol::ACTION2] = 3;
    
        fsm[3][Symbol::ACTION1] = 3;
        fsm[3][Symbol::ACTION2] = 3;
    
        std::vector<std::function<void()>> actions;
    
        // parse command line
        int i = 1;
        int state = 0;
        while (i < argc)
        {
            std::string arg{argv[i++]};
            size_t equals = arg.find('=');
            if (equals != std::string::npos)
            {
                std::string key = arg.substr(0, equals);
                std::string value = arg.substr(equals + 1);
                Symbol symbol = Symbol::NA;
    
                if (key == "sleep")
                {
                    symbol = Symbol::SLEEP;
                }
                if (key == "target")
                {
                    symbol = Symbol::TARGET;
                }
                if (key == "a")
                {
                    symbol = Symbol::ACTION1;
                }
                if (key == "b")
                {
                    symbol = Symbol::ACTION2;
                }
    
                if (symbol == Symbol::NA)
                {
                    exitApp("Unkown arg");
                }
                if (!fsm[state].count(symbol))
                {
                    exitApp("Not allowed arg");
                }
    
                if (symbol == Symbol::SLEEP)
                {
                    sleep = std::stoi(value);
                }
                if (symbol == Symbol::TARGET)
                {
                    target = value;
                }
                if (symbol == Symbol::ACTION1)
                {
                    actions.push_back([=]()
                                      { action1(target, std::stoi(value)); });
                    actions.push_back([=]()
                                      { action3(sleep); });
                }
                if (symbol == Symbol::ACTION2)
                {
                    actions.push_back([=]()
                                      { action2(target, std::stoi(value), 1000); });
                    actions.push_back([=]()
                                      { action3(sleep); });
                }
    
                state = fsm[state][symbol];
            }
        }
        if (actions.empty())
        {
            exitApp("No args ...");
        }
        actions.pop_back(); // Pop last unneeded action
    
        // Perfom actions
        for (const auto &action : actions)
        {
            action();
        }
    
        return 0;
    }
    

    Langsam mag ich C++. 😅



  • enum class Action bräuchte man jetzt auch nicht mehr (übersehen)...

    Könnte mir jemand

                    actions.push_back([=]()
                                      { action1(target, std::stoi(value)); });
    

    im Einzelnen erklären? Wie funktioniert das? Ginge es auch mit weniger Zucker? Ist das das 1:1-Äquivalent zum Java-Code oben?



  • @Lennox

    Da passieren mehrere Dinge...

    [=]()
    { 
       action1(target, std::stoi(value));
    }
    

    erzeugt ein anonymes Lambda ohne Funktionsparameter, das die Parameter target und value als Kopie bindet. Beim Aufruf des Lambda wird die Funktion action1 mit den beiden Parametern aufgerufen.
    Das Lambda selbst wird anschließend in ein std::function<void()> gekapselt und in den Vektor eingefügt.



  • @DocShoe Danke, funktioniert diese Funktionsparameterbindung intern/automatisch? Gibt es noch Alternativen, um die Funktion, Paramter und den Aufruf zu kapseln?

    Btw., @Schlangenmensch anstatt size_t sollte std::size_t verwendet werden.



  • @Lennox Und warum? Normalerweise hätte ich const auto geschrieben, wollte aber im Vergleich zu Java die Typen explizit hinschreiben.

    Mit dem Capture [=] gibst du an, dass das Lambda alle lokalen Variablen, die das Lambda nutzt als Kopie übernehmen solle. Kopie ist der default. Du kannst auch angeben, was genau Verfügbar sein soll:

    [target, value]()
    { 
       action1(target, std::stoi(value));
    }
    

    Werte die als Kopie gekapselt werden sind auch direkt const in dem Lambda. Wenn du die im Lambda verändern willst, musst du das explizit mutable machen

    [target, value]() mutable 
    { 
       action1(target, std::stoi(value));
    }
    

    Du kann mit & Werte als Referenz übergeben (target wird als Referenz gebunden)

    [&target, value]()
    { 
       action1(target, std::stoi(value));
    }
    

    und ein Lambda kann Parameter haben, die beim Aufruf übergeben werden:

    [value](const string& target)
    { 
       action1(target, std::stoi(value));
    }
    

    Wie ich weiter oben geschrieben habe, kannst du Funktionen mit den Parametern auch mit std::bind in std::function kapseln:

    actions.push_back(std::bind(action2, target, std::stoi(value), 1000));
    

    Das hier:

    runnables.add(() -> action2(finalTarget, finalNum, 1000));
    

    ist doch in Java auch ein Lambda, was du da hinzufügst, oder? Daher sollte das mit dem Lambda schon recht "äquivalent" sein, wobei ich mich bei Java nicht mit den Feinheiten auskenne.



  • @Schlangenmensch sagte in Wie am besten Java in C++ Programm umwandeln?:

    () -> action2(finalTarget, finalNum, 1000)

    Das ist eine statische, anonyme Klasse, welche das Runnable Interface realisiert.

    https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html

    Das Interface hat nur eine Methode, die implementiert werden muss, daher ist die Lambda-Schreibwese möglich.

    Innerhalb der Methode innerhalb der Lambda-Klasse wird einfach nur action2 aufgerufen, deshalb können wir uns das return... usw. sparen.

    Eigentlich sind das hier sogar zwei verschränkte Lambdas, die hier zum Einsatz kommen, eins für die implementierende Klasse und eins für die implementierte Methode.

    Ich vermute, die Parameterbindung verhält sich hier zu 1:1 wie actions.push_back(std::bind(action2, target, std::stoi(value), 1000)); - sprich, die Ausdrücke sind quasi Äquivalent (nur, dass es in Java natürlich keine Referenzen gibt, da call-by-value... deshalb wird auch verlangt, dass die eingehenden Parameter, also Variablen, final sind).

    Hilft dir das erst mal?


Anmelden zum Antworten