Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

51

21.06.2014, 10:34

Na ja, nee. Du hast noch immer einen einzigen Syntax-Baum, da du ja deine "Mini-Trees" irgendwo in Reihenfolge ablegst und in Reihenfolge abfrühstückst. Deine "Mini-Tree"-Liste entspricht halt einfach einem Knoten <S> ::= <ExprStmt>*.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

52

21.06.2014, 18:57

Hmm wo ich noch unsicher bin wie ich es am schönsten löse: verschiedene Datentypen.

Aktuell denke ich an sowas:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Var{
            public:
                union val{
                    bool b;
                    int i;
                    float f;
                    std::string* str;
                };

                INSTRUCTIONS type;
                val value;

                Var(const bool b){
                    type = BOL;
                    value.b = b;
                    value.str = 0;
                }

                Var(const int i){
                    type = INT;
                    value.i = i;
                    value.str = 0;
                }

                Var(const float f){
                    type = FLT;
                    value.f = f;
                    value.str = 0;
                }

                Var(std::string &s){
                    type = STR;
                    value.str = &s;
                }

                ~Var(){
                    delete value.str;
                }
        };


Oder Polymorphie, blöd ist nur dass man ständig überall Verzweigungen hat.

Gute Idee? Wie hab ihrs gemacht?

[Edit]

Mir fällt gerade noch eine andere Möglichkeit ein. Ich speichere den Typ und einen Index. Für jeden Datentyp (4 Stück) gibt es dann einen vector.
Dann kann ich darin indiziert lesen/schreiben, habe allerdings immer noch die Verzweigungen.

So in der Art:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
class Var{
    INSTRUCTIONS type; // INT, FLT, BOL, STR
    std::size_t index;
}

vector<int> ints;
vector<float> floats;
// ...
vector<Var*> vars;
stack<Var*> stack;

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »DeKugelschieber« (21.06.2014, 19:31)


Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

53

21.06.2014, 19:31

Die grundliegende Technik hast du bereits angedeutet: Man nehme einen union aller relevanten Daten-Typen und einen zusätzlichen Typ-Flag. Jetzt ist natürlich neben der Wahl der Datentypen und ihrer Layouts die Frage, wie du den Flag verpacken willst. Eine beliebte Strategie die z.B. MRuby und Lua nutzen ist das NaN-Boxing. Da bei NaN das Aussehen der Mantisse undefiniert ist und unvorsichtige Rechenoperationen bloß wieder NaN erzeugen, kann man die Bits der Mantisse gleichzeitig für Flags und vielleicht auch immediate Daten wie int32_t verwenden.

Eine Technik die ich bei 64bit-Systemen in Onyx verwende ist die, dass alle durch Onyx allokierte Objekte 64Byte-Alignment haben. (8*sizeof(void*)) Dadurch sind die untersten 6bit frei für Flags. Ich erhalte also Objekt-Pointer, indem ich die Flags wegmaskiere, und ich erhalte immediate-Daten wie Symbole, Integer, und Floats über die höherwertigen Bytes. (Onyx nutzt floats für Immediates.) Bei 32bit-Systemen fahre ich momentan die extrem ineffiziente Schiene, dass Pointer und Immediates die höheren Bytes eines uint64_t einnehmen und die Flags die unteren 32bit verschwenden, was insgesamt zwei Register statt einem blockiert. Vielleicht denke ich mir da bei Zeiten was aus.

(std::string*? Ernsthaft? D: )

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

54

21.06.2014, 21:58

Ich halte die Variant Geschichte (also die union Lösung) für unschön. Die Lösung von Evrey mit den Typ-Flag wäre auch meine erste Wahl.

edit:
@Evrey: Was hast du gegen std::string*? Wenn er die Variant Lösung verwenden würde, wäre das die einzige Möglichkeit den string zu speichern. :P
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

55

21.06.2014, 22:44

Tagged unions sind doch nichts Anderes als meine Lösung. ôO Ein Union, dessen Layout abhängig vom Aussehen eines 64bit-Pointers ist. Ob du jetzt über union-Member fummelst oder selbst shiftest und castest kommt aufs Gleiche.

Bezüglich des String-Pointers: Ich habe nichts gegen sie. Ich war bloß überrascht. Es ist ungewöhnlich. std::vector, std::string & Co. sind derart kompakte Container, die selbst wieder Pointer verwalten, dass es eher üblich ist, sie brav auf dem Stack zu lassen und vielleicht mal per Referenz zu übergeben. Solche Objekte im Heap sind ein für mich seltener Anblick.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

56

21.06.2014, 22:55

Da bin ich wohl altmodisch, ich fummel nicht sogern mit unions rum... :huh:

Achso. Naja geht bei 'nem union ja nicht anders.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

57

21.06.2014, 23:13

Gut also ich bekomme dafür wohl eine Lösung hin, die Frage war eher: wie elegant?
Mich nervt das hier (pseudo Ausschnitt):

C-/C++-Quelltext

1
2
3
4
5
6
7
if(type == INT){
    a->value.int += b->value.int;
}
else if(type == FLOAT){
    a->value.float += b->value.float;
}
// ...


Alternativ könnte man evt. jeden Operator überladen (+, +=, ==, usw.) wobei man dann ja Ausnahmen extra implementieren könnte (bei Strings z.B.).
Wie sieht euer Interface da aus?

Und wie schwer wäre es im Nachhinein die Typen einzuführen? Ich vermute nicht allzu schwer, aber wenn ich mich irre wärs doof :P
Gerade mache ich einfach mal mit den Kontrollstrukturen weiter. Z.B. Bedingungen, ist ganz witzig wenn man verstanden hat wie parsing funktioniert:

Quellcode

1
_x = 1+(1 < 2) # ergibt 2 :D

Techie

Alter Hase

Beiträge: 717

Wohnort: Bayreuth

Beruf: Student | Hilfswissenschaftler in der Robotik

  • Private Nachricht senden

58

21.06.2014, 23:14

Hört sich interessant an ( habe nur den ersten Post gelesen :thumbsup: ).

Ich arbeite so nebenbei an einem Frontend für NASM.

BTW:
Hat da jemand eigentlich 'ne gute Idee wie ich, das gut objekt-orientiert darstellen kann?
I write my own game engines because if I'm going to live in buggy crappy filth, I want it to me my own - Ron Gilbert

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Techie« (22.06.2014, 01:28)


Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

59

22.06.2014, 03:01

@Kulli: Ob du überlädst oder nicht, du kommst an solchen Abfragen nicht vorbei. Du könntest höchstens bei komplexeren Objekten die VTable ausnutzen, aber das wird dir bei Immediates auch nicht helfen. Es ist halt etwas hässlich, aber dafür ist der entsprechende Code tief in der VM versteckt und besudelt so nicht das Antliz des sichtbaren APIs. In meiner ersten und uralten Fassung der OnyxVM hatte ich damit experimentiert, Typ-Tags zweier Handles ineinander zu shiften. Das sollte Doppel-if-Ketten ersparen. Der Gewinn dadurch war nicht gerade nennenswert, weshalb ich - wie jede andere VM deren Code ich gelesen habe - bei genau dem bleibe, das du da pseudocoded hast.

@Techie: Klassenbasiert oder Prototypenbasiert. (Die Frage ist rhetorisch.) Mal davon abgesehen, dass ich keine Ahnung habe, was du genau wie darstellen willst.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Techie

Alter Hase

Beiträge: 717

Wohnort: Bayreuth

Beruf: Student | Hilfswissenschaftler in der Robotik

  • Private Nachricht senden

60

22.06.2014, 16:08

@Evrey: Meine Artikulationskünste, yey. Ich meinte, mir fällt es schwer alles zufriedenstellend in eine Klasse zu packen. Habe eine parser-,assembler- und Linkerklasse.
Die Parserklasseninstanzen erstellen dann unabhängige Assemblerdatei. Nach und nach wird der Code dann von den Instanzen "verlinkt" ( subcalls etc. ).
I write my own game engines because if I'm going to live in buggy crappy filth, I want it to me my own - Ron Gilbert

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Techie« (22.06.2014, 16:37)


Werbeanzeige