Dipl.-Ing. Martin Weitzel, 64380 Roßdorf bei Darmstadt

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;
}