C++Guns – RoboBlog

02.02.2018

C++ Guns: Generic Data Type Design Pattern - Teil 1

Filed under: Allgemein — Tags: — Thomas @ 15:02

Hmm das Problem lässt sich vermutlich am besten anhand von Beispielen erklären.

Wie immer nehm ich gern mein Point2D Datentyp her. Es gibt ja verschiedene Wege so etwas zu implementieren.
Eine class mit privaten x/y Variablen und entsprechende getter/setter. Oder ein struct mit nur public x/y Variablen:

strcut Point2D {
 double x, y;
}

Das Problem bei dieser Implementierung ist, dass die x/y Variablen nicht in einem Array sind. Das mag vllt. auf den ersten Blick unnötigt erscheinen, aber im laufe der Jahre habe ich den Zugriff auf xyz anhand von Indizes 1,2,3 öfter mal gebraucht. Die Alternative wäre also:

strcut Point2D {
  std::array<double,2> xy;
}

Aber hier fehlt dann die Möglichkeit gut auszudrücken, dass man explizit auf die x-Variable zugreifen möchte. Wie schnell hat man doch die Index Zahlen verdreht. Eine Kombination beider Variablen ist auch Möglich:

strcut Point2D {
  std::array<double,2> xy;

  auto& x() {
    return operator[](0);
  }

  auto& y() {
    return operator[](1);
  }
}

Die const Varianten der x() y() habe ich jetzt mal weg gelassen. Ich möchte jetzt auf etwas anderes hinweisen: Wie sieht es aus wenn man dieses Konstrukt in seinem Code nutzt:

  Point2D p;
  p[0] = 10;
  p[1] = 20
  cout << p.x() << p.y();
  p.xy[0] = 100;
  p.xy[1] = 200;
  p.xy = {3,4}; ???
  cout << p. xy; ???
  p.x() = 1000;
  p.y() = 2000;

Also ganz ehrlich: Nein. Das ist zu viel des Guten. Zu viele Freiheitsgrade. Das verwirrt doch nur. Und wer die Qual der Wahl hat, trifft die falsche Entscheidung. Vor allem, muss man wirklich die Koordinaten über die Funktion x() und y() setzten können? Das sieht auch ungewohnt aus. Und die extra Variable xy erscheint auch unnötig und überflüssig. Zugriff auf Member, über Funktionen, über Index Operator. Uneinheitlich!

Vor ein paar Monaten bin ich ja auf noch eine weitere Möglichkeit gestoßen, diesen Datentyp zu implementieren. Siehe C++ Guns - C++17 Extension to aggregate initialization. Und ich muss sagen, so schlecht ist das gar nicht. Es bietet eine Menge.

Die Idee ist, den Point2D Datentyp von einem std::array erben zu lassen. Und für die bessere Lesbarkeit noch getter Funktionen mit guten Namen bereit zu stellen. Durch die Vererbung IST der Point2D Datentyp ein Array. Und bietet automatisch den Index Zugriff an. Und durch die getter Funktionen bekommen die einzelnen Arraypositionen eine Semantik.

strcut Point2D : public std::array<double,2>
{
  const auto& x() const {
    return operator[](0);
  }

  const auto& y() const {
    return operator[](1);
  }
}

Nun gestaltet sich die Nutzung schon einfacher. Das Setzten der Werte passiert über eine Initialisierungsliste. Das Lesen über Funktionen. Und in den Fällen wo ein Indexzugriff besser ist, ist das nun auch realisierbar.

  Point2D p {1,2};
  cout << p.x() << p.y();
  p[0] = p[1];

Hier habe explicit nur const getter Benutzt. Dazu sage ich später noch mehr.

Im nächsten Teil werde ich Versuchen, dieses Muster auf andere Datentypen anzuwenden.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress