[C++] Szablon funkcji operatora, jest takie cos?

0

Otóż pisze własnie klase która reprezentuje zmienną i może ona zmieniać swoj typ podczas działania programu (np z int na char i odwrotnie).
Gdy zabrałem sie za pisanie do nich operatorów okazało się to strasznym tasiemcem, a każda zmiana kodu będzie się ciągneła lawinowo. Część listingu funkcji dla operatora +:
Objaśnienia:
-'zmienna' dziedziczy 'wartosc' publicznie

  • nazwy Integer, Char... sa typu enum
  • skladowa 'dane' to 'void*'
    Zmienna operator+ (Wartosc& z1, Wartosc& z2) 
    {
    Zmienna temp; 
    switch (z1.typ)
    {
    case Integer:
        switch (z2.typ)
        {
        case Integer:
            temp.typ = Integer;
            temp.dane = new int(*((int*)z1.dane) + *((int*)z2.dane));
            break;
    /* tu jeszcze sie znajduja 4 typy*****************/
        case Char:
            temp.typ = Integer;
            temp.dane = new int(*((int*)z1.dane) + *((char*)z2.dane));
            break;
        }
        break;
    case Float:
        switch (z2.typ)
        {
        case Integer:
            temp.typ = Float;
            temp.dane = new float(*((float*)z1.dane) + *((int*)z2.dane));
            break;
        case Float:
    /* itd ***********************/

    Ogólnie klasa może przyjmować 6 róznych postaci.
    Kod jednego operatora zajmuje mi 160 linijek... -.-
    Jest jakiś sposób żeby zwalczyć tego tasiemca?

0

Myślę, że dodawanie nie powinno być operacją na Twoim typie. Myślę, że zainteresują Cię biblioteki boost::variant oraz boost::any.

Jeżeli masz n typów, to jest n^2 par. Jeżeli operacje na każdej parze wykonuje się inaczej to nic nie poradzisz. Trzeba napisać wszystko.
Jeżeli są podobne kawałki kodu to je zaznacz i zastanów się jak zmniejszyć liczbę operacji.

Najłatwiejszy sposób to stworzenie funkcji dodającej double i double. Następnie tworzysz dwie zmienne
double a, b;
Do a przypisujesz wartość z1, do b wartość z2. Dodajesz (jako double) i ponownie konwertujesz na typ wynikowy.

Tyle, że w ten sposób dodawanie kolejnych typów może być trudne (szczególnie tych zdefiniowanych przez programistę). Proponowałbym zamiast enuma użyć polimorfizmu. Masz klasę Typ i po niej dziedziczą Char, Integer, Double... Każda klasa definiuje dodawanie ze wszystkimi typami jakie zna w ten sposób:

TypWyniku operator+(const Typ2& zm2) {
 // generalnie jakiś kod obsługujący część przypadków
    if (zm2 == char)....
    if (zm2 == integer)....

// jeżeli się nie dopasowało to przekazujemy do zm2 (może drugi typ da radę)
    if (&zm2 != this) 
         zm2.operator+(*this);
    else
          throw BadType;
}

To tak mniej więcej. Jeżeli ani zm2 ani zm1 nie obsłużą to to się zapętli ;-) trzeba dodać sprawdzenie tego przypadku.

0

hm hm.. zeby uproscic sobie owo n2 mozna sprobowac zaprządz do pracy pattern visitor'a pozwalajacy zrobic sobie cos a'la double-dispatch.. wymagac to bedzie poprawnej hierarchi klas zamiast i polimorfizmu. n2 sie nie uniknie, ale za to mozna to uporzadkowac lekko..

0

To może ja napisze w ogóle po co mi to potrzebne. Otóż buduje klase interpretującą kod turbo pascala. Ale to dłuższa bajka. Więc bedzie tu wystepowała klasa wirtualna Wartość. Przechowuje ona informacje o typie oraz oraz wartość zmiennej, wyrażenia, funkcji itp. Nie wiadomo co ona bedzie przechowywać na początku. Będą ją dziedziczyć klasa Zmienna, Wyraznie oraz Funkcja. Zmienna w sumie nic nowego nie dodaje do klasy wartość. Ma jedynie konstruktory pozwalające zainicjować się daną wartością. Klasa Wyrazenie ma składowe dwie klasy typu Wartosc oraz informacje o operacji jaką w stosunku do nich ma zastosowac (+, -, itp.). Po wykonaniu funkcji wirtualnej Aktualizuj oblicza ta wartość najpierw prosząc o to swoje składowe. Funkcja oblicza wartość funkcji i zapisuje jej wartość po wywołaniu funkcji Aktualizuj na niej.
Tak to wygląda w skrócie.

Proponowałbym zamiast enuma użyć polimorfizmu. Masz klasę Typ i po niej dziedziczą Char, Integer, Double... Każda klasa definiuje dodawanie ze wszystkimi typami jakie zna w ten sposób:

Hmm, pomyśle nad tym ;)
Ogólnie myśle że można zrobić w klasie Wartosc klase składową która bedzie własnie przechowywała ten typ i wartość w formie dziedziczenia. Np klasa wirtualna Typ_i_Wart i od niej dziedzicza klasa Char, Integer itp. A sama klasa Wartość tylko przechowuje informacje wskaźnik. Rozwiązuje to problem niewiedzy typu przy tworzeniu oraz dowolne zmienianie przechowywanego typu w miare potrzeby.

0

a co z operatorami unarnymi? np. -(2*3).

Koniecznie zapoznaj się z wzorcami: Interpreter, Kompozyt oraz Visitor. Myślę, że to rozwieje wszystkie wątpliwości. Zauważ, że skoro tłumaczysz pascala na C++ to możesz to zrobić prawie 1-1.

Zwróć uwagę, że nie musisz definiować Integer+Char, Integer+Float,...
Wystarczy Integer+Integer, Float+Float,... Wystarczy, że dodasz operację rzutowania (tu byłoby automatycznie do szerszego typu).

Jeżeli będziesz chciał dodać typy definiowane przez użytkownika (klasy), to także nie ma problemu (można pomyśleć, że wtedy trzeba robić + na różnych typach). Wtedy w drzewie rozbioru wyrażenia będziesz miał a.operator+(b), czyli zwykłe wywołanie funkcji, a nie a+b, w tym sensie, że a i b są podczepione pod operator +.

Jeżeli masz trochę czasu, to polecam napisać najpierw interpreter prostszego języka (np. Basic jest dobry). Przykład można znaleźć w internetowym podręczniku do OCaml'a (Application development with OCaml).

0

na razie tak na szybko napisalem:

class Dana
{
public:
    Typ typ;
    Dana(Typ t): typ(t) {}

    virtual Dana& operator+(const Dana& z2) const = 0;
    virtual Dana& Dodaj(const int &co) const = 0;   
};

class TInteger : public Dana
{
public:
    int wart;
    TInteger() : Dana(Integer), wart(0) {};
    TInteger(int czym) : Dana(Integer), wart(czym) {};

    Dana& operator+(const Dana& z2) const { return z2.Dodaj(wart); }
    Dana& Dodaj(const int &co) const { return TInteger(wart+co); }
};

teraz mam problem z "return TInteger(wart+co)" ponieważ tworzy to obiekt tymczasowy, nie moge tego przypisać do referencji.
A co do -(23) to najpierw "2" i "3" zostają zamienione w Zmienna, te "2" i "3" zostają wpakowane jako składowe klasy Wyrażenie, który przechowuje również informacje o operatorze. Teraz te Wyrażenie i operator '-' znowu sa wpakowywane w klase Wyrażnie który teraz zawiera obiekt klasy wyrażenie (23) oraz informacje o operatorze który ma wykonać.
Zrobie sobie Turbo Pascala ;)

EDIT: problem rozwiazany: Dana& Dodaj(const int &co) const { TInteger integ(wart+co); return integ; }

0

Z tym -2*3 chodziło mi o:

Klasa Wyrazenie ma składowe dwie klasy typu Wartosc

A tu masz tylko jedną wartość.
Tak samo być może będą operacje o trzech argumentach.

0

No to zainicjowana zostaje jedna z dwóch składowych, a dzieki informacji jaki operator ma działać będzie wiedział ze drugiej składowej ma nie tykać bo tam nic nie ma.
O trzech argumetach? Nie słyszałem ze w TP są takie. Jak ci chodzi o 1+2+3 to można to zamienić na (1+2)+3 itp.

0

Nie wiem jak w Pascalu. W cpp jest np. cond ? then_part : else_part. Operator ?: ma 3 argumenty. Mniejsza z tym. Po prostu zdziwiło mnie, że Operator nie jest interfejsem dla konkretnych operatorów. Zamiast tego masz jeden ogólny operator.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0