Typ wariantowy w C++

0

Witajcie,

znalazłem pewien problem. Tworzę interpreter języka skryptowego na bazie abstrakcyjnego drzewa syntaktycznego (AST) i potrzebny mi jest ekwiwalent typu Variant z Delphi, lub dynamic z nowego C#. Jedną przeszkodą jest to, że C++ jest językiem statycznie typizowanym. Wiadomo, jak można stworzyć taki typ (na pewno do tego będzie potrzebna informacja RTTI, ale myślę, jak by to elegancko rozwiązać)?

1

Sugerowałbym zrobienie struktury połączonej z unią w stylu:

enum ValueType { vtBool, vtChar, vtInt, vtFloat, vtString };

struct Value
{
 ValueType type;

 union
 {
  bool _bool;
  char _char;
  int _int;
  float _float;
  string _string;
 }
}
  • możesz pobawić się w przeładowywanie operatorów, aby umożliwić takie rzeczy:
    ValueType value = "foo";
0

Pytanie brzmi co dokładnie chcesz zrobić. Pomysł z unią jest ok, o ile interesuje cię proste rzutowanie typów. Jak chcesz mieć coś bardziej skomplikowanego to wydaje mi się że zwykły polimorfizm będzie sensowniejszy. Czyli jakaś klasa bazowa a potem różne warianty dla niej.

0

@Patryk27
Fajne rozwiązanie, ale...
Jak potem rozwiązać problem podstawowych operacji np. łączenia stringów, czy zwykłego arytmetycznego dodawania?

@Shalom
Chciałbym aby AST mogło zwracać wynik swojego działania, tylko problemem jest właśnie podstawienie odpowiedniego typu zwracanego. Polimorfizm też nie wydaje mi się zły, ale jest kolejny problem. Jak wykonywać operacje, jeśli w czasie kompilacji nie wiemy, jaki jest podstawiony konkretny typ?

1

Jak potem rozwiązać problem podstawowych operacji np. łączenia stringów, czy zwykłego arytmetycznego dodawania?

W pseudokodzie coś w stylu:

operator + (Value A, Value B): Value
{
 Value result;

 if ((A.type == vtInt) && (B.type == vtInt))
 {
  result.type = vtInt;
  result._int = A._int + B._int;
 } else

 if ((A.type == vtInt) && (B.type == vtFloat))
 {
  result.type = vtFloat;
  result._float = A._int + B._float;
 } else

 if ((A.type == vtFloat) && (B.type == vtInt))
 {
  result.type = vtFloat;
  result._float = A._float + B._int;
 } else

 if ((A.type == vtFloat) && (B.type == vtFloat))
 {
  result.type = vtFloat;
  result._float = A._float + B._float;
 }

 if ((A.type = vtString) && (B.type == vtString))
 {
  result.type = vtString;
  result._string = A._string + B._string;
 } else

// if (isOperatorOverloaded(A.type, B.type)) (jeżeli w swoim języku umożliwiasz overloadowanie operatorów, musisz dodatkowo to wziąć pod uwagę)

  throw new InvalidOperatorException(A.typeToString, B.typeToString);

 return result;
}

Sprawę możesz sobie nieco ułatwić makrami.


W przypadku korzystania z klas wyglądałoby to nieco inaczej: ``` class Value { abstract virtual Value Add(Value B); abstract virtual Value Sub(Value B); // i tak dalej } ``` Przykładowo `IntValue` mogłoby wyglądać tak: ``` class IntValue: Value { Value Add(Value B) { Value result;

if ((this.type == vtInt) && (B.type == vtInt)) // int + int
{
result.type = vtInt;
result._int = ...
} else
i tak dalej

return result;
}
}

0

@Japer chciałbyś żeby AST zwracało konkretny typ? To ja bym się zastanowił czy nie da rady wykorzystać tutaj szablonów.

0

@Shalom właśnie chcę, aby wszystkie rzeczy były rozpatrywane w czasie wykonywania. Bo wtedy nie miałoby sensu, gdybym to zrobił w czasie kompilacji.

0

Może to się nada:
http://www.boost.org/doc/libs/1_54_0/doc/html/any.html
nie próbowałem, tylko wiem że coś takiego istnieje.

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