Hallo, ich möchte ein C++ Programm versuchen zu schreiben wo Bedingungen aus verschiedenen vorgefertigten Modulen (Funktionen, Operatoren(> < && ||)) per Zufall zusammengestellt werden und dann ausgeführt werden. Als grobes Beispiel es gibt 100 Funktionen mit Rückgabewerten (genannt FR) und es gibt Operatoren wie < > && || und es gibt weitere 100 Funktionen mit bestimmten Aktionen (genannt FA). Nun soll random daraus eine Kombination erstellt werden können wie zB: if(FR1() > FR2() && FR4 < FR8 && FR1 < FR9) { FA3(); } Meine Frage wäre wie würde man das grundsätzlich angehen von der Datenstruktur? In welcher Datenstruktur erzeugt man so eine Aneinanderreihung von Funktionen und Operatoren die miteinander zur Laufzeit dynamisch kombiniert ausgeführt werden sollen? Und wie setzt man das um damit auch das Programm performant bleibt? Ein Ansatz wäre sicherlich auf Stringbasis die Code-Module nach funktionierenden Regeln zusammenstellen zu lassen und dann zur Laufzeit zu Libs zu kompilieren und einzubinden (zb dlopen()). Etwa wie in dieser Ansatz: https://stackoverflow.com/questions/11016078/is-it-possible-to-create-a-function-dynamically-during-runtime-in-c/11016382#11016382 Meine Frage wäre, geht so etwas auch ohne besondere Perfomance-Verluste direkt in C++ mit Hilfe irgendwelcher dynamisch erstellten Datenstrukturen welche dann den entsprechenden Codeablauf von Funktionen und Operatoren abgearbeiten können? Wo packe ich sozusagen die Kombination solcher Module rein was als Ganzes dann natürlich noch ausführbar sein soll? Grüße
Du hast was prinzipielles bei compilieren vs. Laufzeit nicht verstanden. CPPDYN schrieb: > Meine Frage wäre wie würde man das grundsätzlich angehen Man programmiert einen Interpreter.
Was du suchst ist eine "embedded scripting language", sprich eine Skriptsprache die eingebunden und ausgeführt werden kann. Eine Liste solcher Sprachen gibt es etwa hier: https://github.com/dbohdan/embedded-scripting-languages Der prominenteste Vertreter ist wohl Lua, der vor allem in der Spieleindustrie sehr häufig zum Einsatz kommt. Zu Lua würde ich auch persönlich raten weil: -) Der Core sehr klein ist. (~200kB) -) Die Community groß und die Entwicklung im Gegensatz zu vielen anderen Vertretern auf der Liste aktiv ist. -) Die C++ Bindings in Form von sol2 (https://github.com/ThePhD/sol2) so ziemlich das Beste sind was es gibt. /edit Deutsche Sprache...
:
Bearbeitet durch User
Kannst auch mal nach dem Command-Pattern suchen, ein ähnliches Problem hab ich mal damit gelöst. merciless
Klingt interessant allerdings hab ich quasi nur von C++ eine Ahnung. Wäre das was ich will nicht auch direkt in C++ möglich ohne auf eine andere Sprache zurückgreifen zu müssen? Performancemäßig klingt das auch nicht gut: "Interpretierter Code ist in etwa fünf bis 20 Mal langsamer als kompilierter Code" https://de.wikipedia.org/wiki/Interpreter#Eigenschaften
In einem Array aus Funktionszeigern alle 100 Funktionen ablegen und dann diese per Zufall aufrufen? Natürlich müssen dann diese Funktionen die gleiche Signatur haben.
Beitrag #6330400 wurde von einem Moderator gelöscht.
Dirk K. schrieb: > Kannst auch mal nach dem Command-Pattern > suchen, ein ähnliches Problem hab ich mal > damit gelöst. > > merciless Meinst du damit OOP also per virtuelle Funktionen etc etwas bauen?
CPPDYN schrieb: > if(FR1() > FR2() && FR4 < FR8 && FR1 < FR9) > { > FA3(); > } Bei dem Durcheiander voon Funktionsaufrufen und Variablen habe ich so meine Zweifel ... > Meine Frage wäre wie würde man das grundsätzlich angehen von der > Datenstruktur? ... aber schau dir mal https://www.gnu.org/software/libmatheval/ an. Du baust einen String zusammen, der dann interpretiert wird. leo
zitter_ned_aso schrieb: > In einem Array aus Funktionszeigern alle 100 Funktionen ablegen und dann > diese per Zufall aufrufen? > > Natürlich müssen dann diese Funktionen die gleiche Signatur haben. interessanter Ansatz das wäre sogar ein performant, aber wie würde man dann das mit den Verknüpfungen der Funktionen per Operatoren machen? Muss ich nochmal überlegen...
CPPDYN schrieb: > dann das mit den Verknüpfungen der Funktionen per Operatoren machen? Genauso mit Zeigern? Erst mal Funktionen erzeugen:
1 | |
2 | bool foo_and(int (fp1)(void), int (fp2)(void)){ |
3 | return fp1() && fp2(); |
4 | }
|
5 | |
6 | bool foo_or(int (fp1)(void), int (fp2)(void)){ |
7 | return fp1() || fp2(); |
8 | }
|
9 | |
10 | usw. |
Und dann auch diese in einem Array von Funktionszeigern unterbringen. Aber wie kann es CPPDYN schrieb: > und es gibt weitere 100 > Funktionen mit bestimmten Aktionen (genannt FA). 100 Funktionen geben? Da steht doch eine if-Anweisung. Und diese hat nur zwei Zustände (true/false). Wie können diese 100 Fuktionen auf zwei Zustände verteilt werden? Auch per Zufall?
zitter_ned_aso schrieb: > 100 Funktionen geben? Da steht doch eine if-Anweisung. Und diese hat nur > zwei Zustände (true/false). Wie können diese 100 Fuktionen auf zwei > Zustände verteilt werden? Auch per Zufall? war natürlich erstmal nur ein Beispiel, aber ja im Grunde alles per Zufall, aus den 100 Funktionen mit Rückgabewert wird in Kombi mit zufälligen Operatoren eine Bedingung gemacht bei der auch wiederum zufällig sein kann wieviele Bedingungen aneinandergehängt werden (durch zB && ||). Wenn dann Bedingung erfüllt ist, dann soll eine der Aktionsfunktionen ausgeführt werden die auch wiederum zufällig gewählt ist... Interessanter Ansatz mit den Funktionszeigern weil das auch schön low-level ist. Die Frage wäre auch ob man dann auch langfristig Verzweigungen (verschachtelte ifs... erzeugen könnte). Muss später mal ein paar Sachen testen... :-)
CPPDYN schrieb: > aber ja im Grunde alles per > Zufall, aus den 100 Funktionen mit Rückgabewert wird in Kombi mit > zufälligen Operatoren eine Bedingung gemacht bei der auch wiederum > zufällig sein kann wieviele Bedingungen aneinandergehängt werden (durch > zB && ||). Wenn dann Bedingung erfüllt ist, dann soll eine der > Aktionsfunktionen ausgeführt werden die auch wiederum zufällig gewählt > ist... Klingt nach Kunst.
CPPDYN schrieb: > Dirk K. schrieb: >> Kannst auch mal nach dem Command-Pattern >> suchen, ein ähnliches Problem hab ich mal >> damit gelöst. >> >> merciless > > Meinst du damit OOP also per virtuelle Funktionen etc etwas bauen? Hier ein Beispiel (ziemlich Pseudocode): Ich habe damit komplexe mathematische Formeln flexibel in einer DB abgelegt und zur Laufzeit die Formel mit Hilfe von einzelnen Commands zusammengestöpselt. Jede Formel lag als Baumstruktur in einer Tabelle abgelegt. So konnte man durch Änderungen in der DB die Berechnungsvorschrift ändern, ohne neu kompilieren zu müssen. Hochperformant ist das natürlich nicht, aber sehr flexibel.
1 | // Calculator-Interface |
2 | class ICalculator |
3 | { |
4 | public: |
5 | double Calc(); |
6 | } |
7 | |
8 | // Addition |
9 | class Add : public ICalculator |
10 | { |
11 | private: |
12 | ICalculator * left; |
13 | ICalculator * right; |
14 | |
15 | public: |
16 | Add(ICalculator * left, ICalculator * right) |
17 | { |
18 | this->left = left; |
19 | this->right = right; |
20 | } |
21 | |
22 | virtual double Calc() |
23 | { |
24 | return left->Calc() + right->Calc(); |
25 | } |
26 | } |
27 | |
28 | // Multiplikation |
29 | class Sub : public ICalculator |
30 | { |
31 | private: |
32 | ICalculator * left; |
33 | ICalculator * right; |
34 | |
35 | public: |
36 | Sub(ICalculator * left, ICalculator * right) |
37 | { |
38 | this->left = left; |
39 | this->right = right; |
40 | } |
41 | |
42 | virtual double Calc() |
43 | { |
44 | return left->Calc() - right->Calc(); |
45 | } |
46 | } |
47 | |
48 | // const Value |
49 | class Const : public ICalculator |
50 | { |
51 | private: |
52 | double value |
53 | ICalculator * right; |
54 | |
55 | public: |
56 | Const(double value) |
57 | { |
58 | this->value = value; |
59 | } |
60 | |
61 | virtual double Calc() |
62 | { |
63 | return value; |
64 | } |
65 | } |
66 | |
67 | // Anwendung (die magic numbers stammten natürlich |
68 | aus Variablen, die zur Laufzeit geändert wurden) |
69 | // result = (4711-27) + 42; |
70 | ICalculator * calc = new Add(new Sub(new Const(4711), new Const(27)), new Const(42)); |
71 | double result = calc->Calc(); |
merciless
Wenn es dir ums lösen von mathematischen Ausdrücken geht, kannst du z.B. auch GNU Octave einbinden und das die Auswertung erledingen lassen. Oder Python. Bei beiden kannst du dir die Implementierung anschauen, wie man einen Interpreter schreibt.
Es kommt sehr darauf an was du genau erreichen möchtest, einfache logische Operationen?, das komplette Typenspektrum von C++?, Funktionen mit n Parametern, Nur Ints oder auch Float, Strings? Was bedeutet für dich Performanz, genau so schnell wie kompiliert? Wenn du dein Ziel so abstrakt laesst ist es schwierig dir konkrete Tips zu geben Falls es nur einfache Operationen sind könntest du damit den Code dynamisch erzeugen, mit relativ gleicher Performanz https://asmjit.com
Das geht prinzipiell auch per Textfresser: 2 Coderümpfe erstellen, und den random-Code dazwischenschieben, compilieren, ausführen. Aber ich fürchte, ich weiss nicht, wozu, also kann ich auch nicht ahnen, worauf es Dir ankommt.
@TO: Sieh Dir mal Racket an (oder andere interpretierte Sprachen, die erstklassige Funktionsobjekte unterstützen).
Das haben einige sehr erfolgreich bereits umgesetzt, ist lange her. Herausgekommen sind dabei so Sachen wie Python , Java , Perl , Lua ... Nur zu: richtig gute C++ Programmierer welche das Rad nochmals erfinden sind immer gefragt!
Programmiersprachentheaterintendant schrieb: > Das haben einige sehr erfolgreich bereits umgesetzt, ist lange her. > Herausgekommen sind dabei so Sachen wie [...] , Perl , > [...] ... Ich habe es gewusst, Perl ist eine rein zufällige Kombination von Operatoren und Funktionen!
Nimm einen std::vector auf std::function<> und shuffle() den, dann schreib für die Operatoren irgendwas, was zwei Argumente nimmt und einen zufälligen Operator drauf anwendet. Wenn das was du beschreibst tatsächlich alles ist was du machen willst, gibt es wirklich einen Haufen einfache Lösungen für das Problem.
Sven B. schrieb: > Nimm einen std::vector auf std::function<> Mit Lambdas kombiniert macht das richtig Spaß. Sowas ist natürlich unmöglich zu debuggen.
LLVM unterstützt JIT-Kompilierung. Ist wahrscheinlich nur völliger Overkill.
mh schrieb: > Programmiersprachentheaterintendant schrieb: >> Das haben einige sehr erfolgreich bereits umgesetzt, ist lange her. >> Herausgekommen sind dabei so Sachen wie [...] , Perl , >> [...] ... > > Ich habe es gewusst, Perl ist eine rein zufällige Kombination von > Operatoren und Funktionen! Natürlich stimmt das so, auch wenn Du nur die Syntax meinst :-) (ich auch!) Die Zufälligkeit ist aber auch ganz bestimmt der interessante/schwierige Teil an der Fragestellung des TOs...
CPPDYN schrieb: > Klingt interessant allerdings hab ich quasi nur von C++ eine Ahnung. Nein. Hast du nicht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.