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

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

1

07.10.2015, 23:10

Unterschiedliche Rückgabewerte für virtuelle Methode

Ich hab' grad ein kleines Denk-Problem und vielleicht weiß ja jemand einen guten Rat.
Ich beschäftige mich gerade (wieder mal) ein wenig mit Compiler Grundstrukturen. Dabei bin ich auf folgendes Problem beim Design-Entwurf gestoßen: Bisher habe ich eine abstrakte Oberklasse Expr die eine virtuelle eval Methode anbietet. Momentan gibt sie einfach einen float zurück, womit Arithmetische Operationen möglich sind.

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
#include <iostream>
#include <vector>
#include <memory>
#include <string>

struct Expr {
    virtual float eval() const = 0;
};

struct IntExpr : public Expr {
    int value = 0;
    
    explicit IntExpr(int val) : value(val) { }
    
    virtual float eval() const {
        return this->value;
    }
};

struct FloatExpr : public Expr {
    float value = 0;
    
    explicit FloatExpr(float val) : value(val) { }
    
    virtual float eval() const {
        return this->value;
    }
};

int main() {
    std::vector<std::unique_ptr<Expr>> exps;
    exps.emplace_back(new IntExpr(42));
    exps.emplace_back(new FloatExpr(4.2));
    
    for (auto& exp : exps) {
        std::cout << exp->eval() << std::endl;
    }
}

Das funktioniert problemlos. Doch wenn ich nun eine StringExpr hinzufüge, habe ich ein Problem, denn eig. müsste die eval Methode nun einen std::string& zurückgeben. :)

C-/C++-Quelltext

1
2
3
4
5
6
7
struct StringExpr : public Expr {
    std::string value;
    
    explicit StringExpr(const std::string& val) : value(val) { }
    
    eval ?!
};


Ich dachte schon an einen Visitor, der statt eval das ganze korrekt auflöst, aber auch das klappt nicht ohne Änderungen an den Expressions. Was denkt ihr?
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

2

07.10.2015, 23:18

Wie wäre es, statt eines floats ein Variant zurück zu geben?

http://www.boost.org/doc/libs/1_58_0/doc/html/variant.html

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

3

07.10.2015, 23:24

Daran dachte ich auch schon (wenn auch nicht an den von boost), aber wie soll ich dann automatisch an den korrekten Typ kommen?

Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
struct AddExpr : public BinExpr {
    explicit AddExpr(Expr*, Expr*);

    virtual f32_t eval() const {
        return this->lhs->eval() + this->rhs->eval();
    }
};


eval müsste dann automatisch den korrekten Typ wiedergeben, und das kann afaik selbst ein Variant nicht. Damit würde sich das Problem also nur verschieben.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

4

07.10.2015, 23:27

Musst du nicht so oder so den Typ bestimmen?
Was soll denn passieren, wenn AddExpr 2 StringExpr hält?

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

5

07.10.2015, 23:32

Das sollte schon beim parsen ausgeschlossen werden. Das Grundproblem ist eig. das printen. Die Print Deklaration soll _alle_ möglichen Expression annehmen und ausgeben können. Man könnte auch zwei Methoden in Expr hinzufügen, ein eval (dann fliegt bei der StringExpr eben ein Fehler) und ein print. Aber dann würden z.B. Additionen geprintet statt ausgewertet werden. Also einen cleanen Weg hab' ich noch nicht gefunden.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

6

07.10.2015, 23:38

Hier frage ich mal: Was soll eine arithmetische Operation mit Strings anfangen? Okay, theoretisch könnte man überlegen sie automatisch zu konvertieren, so fern sie nur eine Zahl enthalten. Oder man behandelt es als Syntaxfehler.

Ich glaube du kannst nicht alle Arten von Expressions in einem Interface vernünftig abhandeln. Du hast Zahlen wie ints und floats, und Expressions die nun mal keine sind.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

7

07.10.2015, 23:42

Es geht dir lediglich um eine Ausgabe? Ich kenne mich nicht wirklich mit boost Variant aus, aber beim schnellen drüber schauen hat der streaming support mit an Board.

C-/C++-Quelltext

1
2
3
boost::variant< int, std::string > v;
v = "hello";
std::cout << v << std::endl; // gibt "hello" aus


Gleichzeitig hast du die Möglichkeit dir verschieden Visitor zu basteln:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class times_two_visitor
    : public boost::static_visitor<>
{
public:

    void operator()(int & i) const
    {
        i *= 2;
    }

    void operator()(std::string & str) const
    {
        str += str;
    }

};

// ...
boost::apply_visitor( times_two_visitor(), v ); // v == "hellohello"

Solltest du dir vll mal genauer anschauen, sieht auf den ersten Blick eigentlich passend aus ;)

Mit type_traits und std::enable_if solltest du dir dann sogar recht einfache überladungen basteln können, die für alle arithmetischen Typen gelten.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

8

08.10.2015, 13:37

Hier findet, meiner Meinung nach, Overengineering statt.

Bevor du dir die Frage stellen solltest, was die Funktion zurückgibt, solltest du erstmal ein Typsystem für deine Sprache festlegen. Dann kannst du einfach eine Struktur mit den Typinformationen und einem Zeiger zu den Daten zurückgeben. Ich halte es spontan auch etwas fragwürdig für "IntExpr" und "FloatExpr" unterschiedliche Klassen zu definieren. Lieber Komposition anstatt Vererbung.

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

10

08.10.2015, 14:15

Hier findet, meiner Meinung nach, Overengineering statt.

Bevor du dir die Frage stellen solltest, was die Funktion zurückgibt, solltest du erstmal ein Typsystem für deine Sprache festlegen. Dann kannst du einfach eine Struktur mit den Typinformationen und einem Zeiger zu den Daten zurückgeben. Ich halte es spontan auch etwas fragwürdig für "IntExpr" und "FloatExpr" unterschiedliche Klassen zu definieren. Lieber Komposition anstatt Vererbung.

Habe nicht vor, Overengineering zu betreiben, deswegen frage ich ja hier. :) Wie würdest du das denn lösen? Ich verstehe spontan diesen Satz

Zitat

Dann kannst du einfach eine Struktur mit den Typinformationen und einem Zeiger zu den Daten zurückgeben
als gleichbedeutend mit der Benutzung eines Variants. Aber ich lasse mich ja gerne eines besseren belehren.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Werbeanzeige