krone2019-03
Praktikum Schulung C++ Refresh Fa. Krone
Mittwoch 20. März, Vormittag (Download des Source-Codes)
- Verstehen eines bestehenden Programmbeispiels
- fertigen Sie ggf. kleine Zeichnungen zwecks Veranschaulichung an
- Einfügen eines neuen evq_nodes mit putev
- Entnehmen eines vorhandenen evq_nodes mit getev
- In diesem Rahmen "Aufräumen" des Codes
- Erstellen Sie zwei getrennte Quelltexte:
- Jeder davon soll EINE Variante der bedingten Kompilierung enthalten
- Testet Sie JEDEN dieser beiden Files in einem eigenen Projekt
- KEINE Fehlermeldungen beim Kompilieren als C-Projekt
- (optional: als C++-Projekt mit minimalen Anpassungen)
- *** ALL TESTS PASSED *** bei Ausführung
- Kommentare verbessern
- Wo nötig neue Kommentare einfügen
- Unverständliche Kommentare löschen
- Namensgebung veressern
- Namen mit "weiter" Sichtbarkeit lang und selbsterklärend
- Namen mit "kurzer" Sichtbarkeit dürfen auch knapp sein
- Blockstruktur
- Verwendung geschweifter Klammern vereinheitlichen
- dabei die Einrückungsstruktur überprüfen
- Komplexe Ausdrücke übersichtlicher machen
- runde Klammern dort, wo sie die Lesbarkeit fördern
- aber auch nicht zu viele davon verwenden
- ggf. lokale Hilfsvariablen einführen
=== Zunächst nur bis hierher === mehr kommt später
- Umstellung auf "abstrakter Datentypen" ADT im C-Stil
- Einführung eigener Datentypen für:
- Event (Aufzählungstyp)
- EventQueueNode (bisher evq_node)
- ADT für bisherige globale Variable evgb und evqe
- zu Struktur EventQueue zusammenfassen
- Initialisierungsfunktion mit Zeigerargument (EventQueue_Init)
- als Zeiger an EventQueue_put und EventQuue_get
- Einbau erster C++-Features
-----------------------------------------------------------------------------------------------------------
/*
* ============================================================================
* Event-Queue Data Structure (heap-allocated event nodes) PROOF OF CONCEPT
* ============================================================================
* This is in a single source file ONLY for testing and demonstration.
*
* To use in production code it split into header and implementation files.
*
* CHANGES:
* - make compilable with C++
* - cleaning-up comments
* - improve naming (somewhat)
* - clean-up indendation and curly braces
* - unify curly brace use
* - check indendation
* - clean-up expresions
* - not too many round parantheses
* - not too few round parantheses
* - use nullptr for invalid pointers
* - in assigments
* - in tests
* - consider introducing temporaries
* - verify correct logic in `theQueue.get` and `theQueue.put`
* - turn "white-box" tests into "invariant checks"
* - consequently use Event-names from enumeration
* - introduce abstract data types ADTs for
* - EventQueueNode
* - EventQueue
* - Turn pointer arguments into reference arguments for:
* - EventQueue_init
* - theQueue.get
* - theQueue.put
* - EventQueue_empty
* - EventQueueNode_init
* - Change C library header into C++
* - instead of #include <whatever.h> use <cwhatever>
* - qualify used functions with `std::`
* - Using more "C++ only" features
* - C++ style heap
* - C++ output
* - booleans
* - brace initialisation
* - direct member initialisation
* - rvalue references (in move-constructor and -assignment)
*/
#include <cassert> // `assert` is a macro (so not used as `std::assert`)
#include <iostream>
/*
* Events below are just for demonstration (Think of an oven controller.)
* As in C enumerations are compatible with integers any (small) integral
* numbers may used instead of the `Event`-s below. (If C++ were used as
* "as better C" to compile this some minor adaptions need be made.)
*/
typedef enum Event {
Event_NONE = 1,
Event_TEMP_HIGH = 100,
Event_TEMP_LOW = 101,
Event_DOOR_OPENEND = 200,
Event_DOOR_CLOSED = 222,
Event_HEATER_FAIL = -1,
} Event;
/*
* EMPTY QUEUE:
* +-----+
* | o---->|0
* +-----+
* head pointer
*
* +-----+
* | o---->|0
* +-----+
* tail pointer
*
* ONE ELEMENT QUEUE: event queue node
* +-----------+
* | o------->|0
* 0|<-------o |
* | (aaa) |
* +-----------+
* ^ ^
* +-----+ | |
* | o-------------------+ |
* +-----+ head pointer |
* |
* +-----+ |
* | o---------------------+
* +-----+ tail pointer
*
* TWO ELEMENT QUEUE: event queue nodes
(youngest) (oldest)
* +-----------+ +-----------+
* | o------------>| o------->|0
* 0|<-------o |<------------o |
* | (bbb) | | (aaa) |
* +-----------+ +-----------+
* ^ ^
* +-----+ | |
* | o----------+ |
* +-----+ head pointer |
* |
* +-----+ |
* | o------------------------------+
* +-----+ tail pointer
*
* "N"-ELEMENT QUEUE: event queue nodes
* (younger ........... older)
* +-----------+ "N-2" +-----------+
* | o------- - other - >| o------->|0
* 0|<-------o |< - nodes - -------o |
* | (zzz) | | (aaa) |
* +-----------+ +-----------+
* ^ ^
* +-----+ | |
* | o----+ |
* +-----+ head pointer |
* |
* +-----+ |
* | o------------------------------+
* +-----+ tail pointer
*/
class EventQueueNode {
public:
static int instances;
public:
class EventQueueNode* next{nullptr};
class EventQueueNode* prev{nullptr};
Event event{Event_NONE};
EventQueueNode(Event ev)
: event{ev}
{
++instances;
assert(instances > 0);
}
~EventQueueNode() {
assert(instances > 0);
--instances;
}
EventQueueNode(const EventQueueNode&) =delete;
EventQueueNode& operator=(const EventQueueNode&) =delete;
EventQueueNode(EventQueueNode&&) =delete;
EventQueueNode& operator=(EventQueueNode&&) =delete;
friend class EventQueue;
};
class EventQueue {
static int instances;
EventQueueNode* head{nullptr};
EventQueueNode* tail{nullptr};
public:
EventQueue() {}
~EventQueue() { clear(); }
EventQueue(const EventQueue&) =delete;
EventQueue& operator=(const EventQueue&) =delete;
EventQueue(EventQueue&&);
EventQueue& operator=(EventQueue&&);
bool empty() const;
void clear();
Event get();
bool put(Event);
};
/*
* Above this line: header file
* ============================================================================
* Below this line: implementation file
*/
int EventQueueNode::instances{0};
bool EventQueue::empty() const {
assert((head == nullptr && tail == nullptr)
|| (head != nullptr && tail != nullptr));
return (tail == nullptr);
}
void EventQueue::clear() {
while (head != nullptr) {
const auto p{head};
head = head->next;
delete p;
}
tail = nullptr;
assert(empty());
}
EventQueue::EventQueue(EventQueue&& init)
: head{init.head}, tail{init.tail}
{
init.head = init.tail = nullptr;
}
EventQueue& EventQueue::operator=(EventQueue&& rhs) {
if (this != &rhs) {
clear();
head = rhs.head; rhs.head = nullptr;
tail = rhs.tail; rhs.tail = nullptr;
}
return *this;
}
/*
* BEFORE: event queue nodes
* +-----------+ +-----------+
* other - - - >| o------------>| o------->|0
* nodes - - - -------o |<------------o |
* | (vvv) | | (uuu) |
* +-----------+ +-----------+
* ^
* +-----+ |
* | o----------------------------+
* +-----+ tail pointer
*
* AFTER: event queue nodes handed over to heap
* +-----------+ memory management as
* other - - - >| o------->|0 no longer used space
* nodes - - - -------o |*3* +-----------+*4*
* | (vvv) | |///////////|
* +-----------+ |\\\\\\\\\\\|
* ^ |// (uuu) //|
* +-----+ |*2* +-----:-----+
* | o-------+ :.......(uuu)
* +-----+ tail returns value of event
* pointer *1*
* RETURNS:
* Event zzz in the above case, 0 when the event queue is empty
*/
Event EventQueue::get() {
Event result{Event_NONE};
if (tail != nullptr) {
EventQueueNode* p = tail;
EventQueueNode** resp = (p->prev != nullptr)
? &p->prev->next
: &head;
/*1*/ result = p->event;
/*2*/ tail = p->prev;
/*3*/ (*resp) = nullptr;
/*4*/ delete p;
}
assert((tail == nullptr)
|| (tail->prev == nullptr)
|| (tail->prev->next == tail));
return result;
}
/*
* BEFORE: event queue nodes
* +-----------+
* new space handed | o------- - - - other
* out from heap 0|<-------o |< - - - nodes
* memory management | (vvv) |
* *1*+-----------+ +-----------+
* |\\\\\\\\\\\| ^
* |///////////| | +-----+
* |\\ (www) \\| head +-------o |
* +-----:-----+ pointer +-----+
* (www).....:
* event value set from argument
*
* AFTER: event queue nodes
* +-----------+ +-----------+
* *2*| o------------>| o------- - - - other
* 0|<--------o |<------------o |< - - - nodes
* | (www) |*3* | (vvv) |
* +-----------+ +-----------+
* ^
* |*4* +-----+
* +---------------------------o |
* head pointer +-----+
* RETURNS:
* Nonzero if the node was inserted, zero if not.
*/
bool EventQueue::put(Event ev) {
/*1*/ EventQueueNode* const p{new EventQueueNode{ev}};
// NOTE: new in C++ will terminated the program,
// not return nullptr when no memory
if (head == nullptr) {
assert(tail == nullptr);
head = tail = p;
assert(p->next == nullptr);
assert(p->prev == nullptr);
}
else {
assert(tail != nullptr);
/*2*/ p->prev = nullptr;
/*3*/ p->next = head;
/*4*/ p->next->prev = p;
head = p;
assert(head->prev == nullptr);
assert(tail->next == nullptr);
}
assert(!empty());
return true;
}
void destructor_check(int nput, int nget) {
EventQueue localQueue;
while (nput--) (void) localQueue.put(Event_HEATER_FAIL);
while (nget--) (void) localQueue.get();
}
int main() {
assert(EventQueueNode::instances == 0);
destructor_check(1, 0);
assert(EventQueueNode::instances == 0);
destructor_check(0, 1);
assert(EventQueueNode::instances == 0);
destructor_check(1, 1);
assert(EventQueueNode::instances == 0);
destructor_check(12, 8);
assert(EventQueueNode::instances == 0);
destructor_check(8, 12);
assert(EventQueueNode::instances == 0);
destructor_check(0, 10);
assert(EventQueueNode::instances == 0);
EventQueue theQueue;
/* :::::::::::::::::::::::::::::::: testing empty queue */
/* --------------------------------------- */
assert(theQueue.get() == Event_NONE);
/* --------------------------------------- */
/* :::::::::::::::::::::::::: testing one element queue */
/* --------------------------------------------- */
assert(theQueue.put(Event_TEMP_HIGH) != 0); /* <=== insert 1st */
/* --------------------------------------------- */
/* :::::::::::::::::::::::::: testing two element queue */
/* -------------------------------------------- */
assert(theQueue.put(Event_TEMP_LOW) != 0); /* <==== insert 2nd */
/* -------------------------------------------- */
/* :::::::::::::::::::::::: testing three element queue */
/* ----------------------------------------- */
assert(theQueue.put(Event_HEATER_FAIL) != 0); /* <= insert 3rd */
/* ----------------------------------------- */
/* ::::::::::::::::::: removing two elements from queue */
/* -------------------------------------------- */
assert(theQueue.get() == Event_TEMP_HIGH); /* <==== remove 1st */
/* -------------------------------------------- */
/* -------------------------------------------- */
assert(theQueue.get() == Event_TEMP_LOW); /* <===== remove 2nd */
/* -------------------------------------------- */
/* ::::::::::: removing ANOTHER two elements from queue */
/* ---------------------------------------------- */
assert(theQueue.get() == Event_HEATER_FAIL); /* <== remove 3rd */
/* ---------------------------------------------- */
/* --------------------------------------- */
assert(theQueue.get() == Event_NONE); /* <======= nothing more */
assert(theQueue.get() == Event_NONE); /* <======= nothing more */
/* --------------------------------------- */
/* :::::::::::::::: inserting and extracting another 50 */
{
const int N = 50;
int i = 0;
for (i = 1; i <= N; ++i) {
/* ---------------------------------------- */
assert(theQueue.put((Event)i) != 0);
/* ---------------------------------------- */
}
for (i = 1; i <= N; ++i) {
/* ------------------------------------- */
assert(theQueue.get() == (Event)i);
/* ------------------------------------- */
}
}
/* :::::::::::::::::::::::::::::::::::::::::::::::::::: */
std::cout <<"*** ALL TESTS PASSED ***" << std::endl;
return 0;
}