Wie am besten Java in C++ Programm umwandeln?



  • Nabend, bevor ich noch lange herumraten muss oder bei der KI nachfrage... Wie sähe das Pendant in C++ zu dieser Main-Methode in Java aus?

      private enum Symbol {
        SLEEP1,
        TARGET1,
        ACTION1,
        ACTION2
      }
    
      public static void main(String[] args) {
        if (args.length == 0) {
          exitApp();
        }
    
        long sleep = 100;
        String target1 = "127.0.0.1";
    
        Map<Integer, Map<Symbol, Integer>> fsm = new HashMap<>();
        fsm.put(
            0,
            Map.of(
                Symbol.SLEEP1, 1,
                Symbol.TARGET1, 2,
                Symbol.ACTION1, 3,
                Symbol.ACTION2, 3));
        fsm.put(
            1,
            Map.of(
                Symbol.TARGET1, 3,
                Symbol.ACTION1, 3,
                Symbol.ACTION2, 3));
        fsm.put(
            2,
            Map.of(
                Symbol.SLEEP1, 3,
                Symbol.ACTION1, 3,
                Symbol.ACTION2, 3));
        fsm.put(
            3,
            Map.of(
                Symbol.ACTION1, 3,
                Symbol.ACTION2, 3));
        List<Runnable> runnables = new LinkedList<>();
        int state = 0;
        for (String arg : args) {
          String[] parts = arg.split("=", 2);
          if (parts.length != 2) {
            exitApp();
          }
          String key = parts[0];
          String value = parts[1];
          switch (key) {
            case "target1" -> {
              if (!fsm.get(state).containsKey(Symbol.TARGET1)) {
                exitApp();
              }
              target1 = value;
              state = fsm.get(state).get(Symbol.TARGET1);
            }
            case "sleep" -> {
              if (!fsm.get(state).containsKey(Symbol.SLEEP1)) {
                exitApp();
              }
              sleep = checkAndParse1(value);
              state = fsm.get(state).get(Symbol.SLEEP1);
            }
            case "a", "b" -> {
              switch (key) {
                case "a" -> {
                  if (!fsm.get(state).containsKey(Symbol.ACTION1)) {
                    exitApp();
                  }
                  String finalTarget = target1;
                  int finalNum = checkAndParse2(value);
                  long finalSleep = sleep;
                  runnables.add(() -> action1(finalTarget, finalNum));
                  runnables.add(() -> action3(finalSleep));
                  state = fsm.get(state).get(Symbol.ACTION1);
                }
                case "b" -> {
                  if (!fsm.get(state).containsKey(Symbol.ACTION2)) {
                    exitApp();
                  }
                  String finalTarget = target1;
                  int finalNum = checkAndParse2(value);
                  long finalSleep = sleep;
                  runnables.add(() -> action2(finalTarget, finalNum, 1000));
                  runnables.add(() -> action3(finalSleep));
                  state = fsm.get(state).get(Symbol.ACTION2);
                }
                default -> exitApp();
              }
            }
            default -> exitApp();
          }
        }
        if (runnables.isEmpty()) {
          exitApp();
        }
        runnables.remove(runnables.size() - 1); // Remove last action
    
        // Run all actions
        runnables.forEach(Runnable::run);
      }
    

    Vermutlich ginge es (viel) einfacher, da C++ ja Funktionspointer kennt?



  • @Lennox Wie wäre es, wenn du es erstmal selbst versuchst?



  • @Schlangenmensch
    Ja gut, ich versuche es erst selbst... aber erwartet keine Wunder von mir...

    Die FSM (also das argument parsing...) ist im Prinzip so, dass eine beliebige Reihenfolge von target1 und/oder sleep1 erlaubt ist, aber die a/b-Auflistung immer danach folgen muss...

    Beispiel:
    program.exe target=1.1.1.1 sleep=2000 a=1 b=3 a=1
    Oder auch:
    program.exe a=1
    program.exe a=1 b=3 a=1
    program.exe sleep=2000 a=1 b=3 a=1
    program.exe target=1.1.1.1 a=1 b=3 a=1
    program.exe sleep=2000 target=1.1.1.1 a=1 b=3 a=1

    Jetzt sollte es deutlich sein. Ungültig wäre z. B.: program.exe target=1.1.1.1
    Muster:
    program.exe (sleep=<int_ms>) (target=<ip/host>) [a/b]=<int> ...

    Ich war mir erst nicht sicher, ob man dafür wirklich eine FSM braucht, aber offenbar genügt ein einfacher https://de.wikipedia.org/wiki/Behavior_Tree für die Verarbeitung der Argumente und das Starten der Runnables nicht, oder?



  • @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.


Anmelden zum Antworten