Sol W3

Feedback Bounded Buffer

Feedback-Bausteine, mit arbiträrer Nummerierung.

Compiler-Warnings (Nr. 1)

Compiler-Warnings sollten nicht ignoriert werden. Zum Beispiel: Wenn eine Funktion einen Rückgabe-Typ hat, braucht sie auch auf jedem Pfad ein return Statement (oder ein throw).

BoundedBuffer<T, SIZE> (Nr. 2)

Im Kontext eines Klassen-Templates muss nicht die ganze Template-Id verwendet werden, sofern man die Instanz mit denselben Template-Argumenten verwenden möchte. Konkret am Beispiel von Bounded Buffer: BoundedBuffer ist äquivalent zu BoundedBuffer<T, SIZE>.

Member-Types (Nr. 3)

Member-Types sollten abhängig vom verwedeten Container definiert werden.

using container_type = std::array<T, CAPACITY>;
using value_type = typename container_type::value_type;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using size_type = size_t;
Dies ermöglicht den einfachen Austausch des zugrunde liegenden Containers. Bei diesen Aliases gibt es jedoch eine Einschränkung. Sie sollten bei Rückgabe-Typen verwendet werden, die vom entsprechenden Rückgabe-Typ des verwendeten Containers abhängig sind. Jedoch muss dies nicht immer der optimale Parameter-Typ auf dem Interfaces des BoundedBuffers sein. Dort sollten die Parameter-Typen unabhängig vom verwendeten Container spezifiziert sein. Beispiel:
reference front() {...}
void push(T const & e) {...}
Grund dafür ist folgender. Der zugrunde liegende Container könnte Spezialisierungen aufweisen, welche nicht mit den erwarteten Typen arbeiten. Ein konkretes Beispiel dafür ist std::vector<bool>, welcher eine optimierte spezialisierung ist, die intern mit Bits arbeitet statt einem bool-Array (oder dies zumindest darf). Da auf diese Bits keine Referenz als bool& zurückgegeben werden kann, wird ein Proxy-Objekt zurückgegeben, welches sich gleich verhalten sollte. Würde nun die push_back-Funktion mit dem entsprechenden Typ-Alias arbeiten, müssten Clients Referenzen auf solche Proxy-Objekte als Argumente verwenden, statt den erwarteten bool values.

Repetitive Checks (Nr. 4)

Repetitive Checks zum Zustand des BoundedBuffers, welche in einer Exception resultieren, wenn sie nicht erfüllt sind, könnten in eine passende Funktion ausgelagert werden.

Beispiel, statt:

void push(T const & e) {
  if (full()) {
    throw std::logic_error { "Buffer is full" };
  }
  //push element
}

Besser:

void push(T const & e) {
  throwIfFull();
  //push element
}

void throwIfFull() {
  if (full()) {
    throw std::logic_error { "Buffer is full" };
  }
}

Duplizierung bei Berechnungen (Nr. 5)

Magie bei der Berechnung und Anpassung von Indizes können in eine separate Funktion ausgelagert werden, damit man einen schönen Namen dafür hat. Beispiel: (startIndex + noOfElements - 1) % CAPACITY liest sich nicht so toll wie ein Funktionsaufruf indexOfLastElement().

Fehlende Includes (Nr. 6)

In der Regel werden folgende Includes benötigt:

Unnötige Inclues (Nr. 7)

Es sollte vermieden werden nicht benötigte Includes im Code zu haben.

Include Guard (Nr. 8)

Jedes Header-File benötigt einen Include Guard, damit sich in der gleichen Compilation-Unit, um mehrfache Includes des gleichen Header-Files zu vermeiden.

Last edited March 31, 2016