Wie am besten Java in C++ Programm umwandeln?
-
@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<>();undrunnables.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
runnablesmusst du ein bisschen schauen, da du nur Funktionspointer mit der selben Signatur in einen Container bekommst. Aberstd::functionundstd::bindsind 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, 1Was ist, wenn
stoieinen schlechten Tag hat? Und... dasACTION_ARGSist 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 Actionbrä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?
-
Da passieren mehrere Dinge...
[=]() { action1(target, std::stoi(value)); }erzeugt ein anonymes Lambda ohne Funktionsparameter, das die Parameter
targetundvalueals Kopie bindet. Beim Aufruf des Lambda wird die Funktionaction1mit den beiden Parametern aufgerufen.
Das Lambda selbst wird anschließend in einstd::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_tsolltestd::size_tverwendet werden.
-
@Lennox Und warum? Normalerweise hätte ich
const autogeschrieben, 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
mutablemachen[target, value]() mutable { action1(target, std::stoi(value)); }Du kann mit
&Werte als Referenz übergeben (targetwird 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::bindin 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?