DIPL.-ING. MARTIN WEITZEL, 64380 ROSSDORF, GERMANY

cpp2022W17

MicroConsult C++ 2022 KW 17

Wie am letzten Kurstag angekündigt, werde ich "zum Nachlesen" demnächst nochmal ein ZIP-Archiv bereitstellen, in dem alle Mosterlösungen inklusive der verbindenden Live-Demos enthalten sind, durchnummeriert in der Reihenfolge der Behandlung im Kurs.

Eine Anregung aus dem Feedback zum Kurs habe ich darin schon umgesetzt: Die Beispiele zur "C/C++-Kompatibiliät" und zum praktischen Vorgehen bei einem "C nach C++"-Umstieg in bestehenden C-Projekten habe ich ausgegliedert. D.h. dieser Teil wird sich künftig nur als optionales Kapitel im Anhang der Unterlagen befinden.

Die Aufgaben des Praktikums werden dann mit einem "reinen" C++-Beispiel beginnen.

Ich wünsche Ihnen ein Schönes Wochenende,


Stand der Aufgaben und Lösungen Donnerstagmittag

https://drive.google.com/file/d/12bVCSK-gUUKYz7JPfIsA1YLihQJkW4_G/view?usp=sharing

https://onlinegdb.com/6pSK8qelM

https://onlinegdb.com/94ViqDdXc

https://onlinegdb.com/NM2-CrgYqP

Auch interessant: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

Link zure letzten Übung:  https://onlinegdb.com/vX7ndMZwl

LATEST ARCHIVE: https://drive.google.com/file/d/1EeiluKrgH0LfodrapYIBuUzOR0mg_7Tm/view?usp=sharing

https://onlinegdb.com/zZRCkIdexp

https://drive.google.com/file/d/1Zkeb3JJQ7HV4-H4J_fJDo5t0SyAgyXy8/view?usp=sharing

 

# 1_Counter Overview

## Step_00

Goals:
* Establish common base with C knowledge as prerequisite
* Motivate separation into separate compilation units

Introduces:
* Compilation environment

## Step_01

Prepared:
* added "Test Driven Development" (TDD)
* added "Command Line Interacttion" (CLI)

### Main Stream -> Step_02

Goals:
* Allow compiling with both, C an C++.

Introduces:
* Strict "Definition Reference Model"
* Clean use of `enum` types
* Necessity of casts from `void*` to typed pointers

### Variants (not continued)

* Step_01x: do NOT cast away `const` from global variables
* Step_01y: ...  though it usually works for stack variables
* Step_01z: C-Preprocessor trickery (not really recommended)

## Step 02

(Compiles cleanly with C **OR** C++ used for **ALL** modules)

### Main Stream -> Step_03

Goals:
* Make more flexible with additional global variable `limit`.

Introduces:
* Use of `const` for globals

### Variants (not continued)

* Step_02x: Mix modules compiled with C and C++

## Step_03

(Flexible upper limit via global variable)

### Main Stream -> Step_04

Goals:
Combine global variables `counter` and `limit` into ADT
(abstract data type) `LimitCounter`.

Introduces:
* Position dependent meaning of `const` when applied to pointers
* (Also: "step by step" approach with intermediate compilation)

### Variants (partial solutions)

* Step_03x: intermediate step to be turned into Step_04
* Step_03y: demonstrate use of two instances for CLI testing
* Step_03z: demonstrate everything is still C compatible 

## Step_04

Achieved:
* Clean ADT as C-compatible solution
* demonstrated use of two separate `LimitCounter with TDD

### Main Stream -> Step 05

Goals:
* Turn pointer arguments into references

Introduces:
* Reference arguments
* `const` vs. non-`const` references

### Variants

* Step_04x: Demonstrate references are implemented as pointers
* Step_04y: Clean solution for Step_01z (no trickery)

## Step_05

(ADT `LimitCounter` WITHOUT access protection)

### Main Stream -> Step 06

Enforcing access protection by turning `struct LimitCounter`
into `class LimitCounter`.

Introduces:
* `private` and `public`
* Member functions

### Variants

* Step_05x: Introducing the `friend` mechanism
* Step_05y: special `getter` / `setter` overloading

## Step_06

(ADT `LimitCounter` WITH access protection)

### Main Stream -> Step_07

Guaranteed and flexible Initialization

Introduces:
* Constructors
* Constructor Overloading

### Variants

* Step_06x: Necessity of destructors
* Step_06y: Implementing copy operations
* Step_06z: Move vs. Copy / Move-only types

## Step_07

### Main Stream -> Step_08

Operator Overloading

### Variants

* Step_07x: Best Practices: assignment-combined operators)
* Step_07y: Incoming type conversions (non-`explicit` c'tors)
* Step_08y: Outgoing type conversions ("type-cast" operators)

## Step_08

### Main Stream -> Step_09

`ChainCounter` class using pointers and composition

Introduces:
* Reuse via composition

### Variants

* Step_09x: `ChainCounter` as derived class from `LimitCounter`
* Step_09y: Composition via private base class
* Step_09z: demonstrate limitations of static binding

## Step_09

### Main Stream -> Step_10

`ChainCounter` class using `LimitCounter` as base class

Introduces:
* Base and Derived Classes
* Dynamic Polymorphism

### Variants

* Step_09x: Applying the NVI idiom ("Non Virtual Interface")

## Step_10

### Main Stream -> Step_11

Adding an `ICounter`-Interface

Introduces:
* Abstract classes
* Pure virtual member functions

## Step_12

Chainable Counter with an Interface

 

 

Step 02 -> Step 03

Generelles Ziel: den "oberen Anschlag" des Zählers flexibler machen.

Führen Sie eine neue GLOBALE variable `limit` ein und verwenden Sie diese an allen Stellen, wo bisher ÌNT_MAX` verwendet wurde.

Step 03 -> Step 04

Fassen Sie die globalen Variablen `counter` und  `limit` zu einer Struktor `LimitCounter` zusammen.

Legen Sie EINE globale Variable dieser Struktur an. Modifizieren Sie aber die Funktionen, die bisher auf `counter` und `limit` zugeriffen haben, s, dass Sie als Argument einen Zeiger auf `LimitCounter` oder `const LimitCounter` erwarten und übergeben Sie beim Aufruf die Adresse der globalen `LimitCounter` Variablen.

Step 04 -> Step 05

Verwenden Sie statt des Zeiger-Parameters eine Referenz.

Link zu Zip-File mit Step_00 bis Step_04

https://drive.google.com/file/d/1k8dcedMeP3xkG8oikCWG-nyeyR5vTn55/view?usp=sharing

# 1_Counter Step 0 (action items)

## Review the code and answer some questions:

* **Don't do a full review right now!**
* Allow yourself about the time specified with each step to
  look over the code.
* Generally focus on parts you do NOT easily understand so
  that YOU can ask questions later.
  
### `main.c` (5 Minutes)

* Before you read the comment at the start:
  * Do you encounter any problems **at the syntax level** in
    understanding the source code of this program?
  * Do you understand what the **C Standard Library Functions**
    used in that code are supposed to do?
* With reading the comment at the start:
  * Are you able to **give a rough description** what the
    program is supposed to do even before looking in `counter.h`
    and `counter.c`?

### `counter.h` and `counter.c` (3 minutes)

* Identify which global variable is accessed by the functions
  defined in that module. 
* Does this module do what you would conclude if you had only
  read the comment at its start?

### `Makefile` (1 minute)

* Can you identify the lines in which the the compiler is told
  which version of the C language specification it should use?

You are not supposed to have or develop a deep understanding of
the build specifications in a `Makefile`. Still feel free to ask
questions if you are interested in that topic.

### General

* Can you name one or two differences between C89 and C99 that
  are the reason why `main.c` is "C89" but counter.c is "C99"?
 

--------

main.c

/*
 * ===========================================================================
 * Random Risc Game Simulator (with "counter" implemented as separate module)
 * ===========================================================================
 * This program simulates the outcome of taking random risks, which may cause
 * a loss or increase of your starting capital. The simulation runs until a
 * everything is lost or a certain goal is reached (per default doubling the
 * start capital). Via command line arguments these simulation parameters can
 * be set:
 *  (1) a delimiter for the maximum capital to risk or gain in each step
 *  (2) the start capital as percentage of the goal that should be reached
 *  (3) variation of outcome (see below)
 * The simulation is based on the random number generator of the C standard
 * library. The generator is NOT seeded so that you get reproducible results
 * on repeated runs with the same setting of the first two arguments. It is
 * possible to get different but still reproducible) variants of the outcome
 * for a given setting of the first two parameters by varying the third one,
 * which determines whether the next risk taken will be a win or a loose.
*/

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#include "counter.h"

int main(int argc, char* argv[]) {
    int const n = (argc > 1) ? atoi(argv[1]) : 5;       /* risk delimiter */
    double const m = (argc > 2) ? atof(argv[2]) : 50;   /* start position */
    int const b = (argc > 3) ? atoi(argv[3]) : 0;       /* win/loose switch */
    int runs = 0;
    enum { UP = '+', DOWN = '-' } dir = UP;
    counter = (int)(m/100 * INT_MAX);
    do {
        int r;
        for (r = rand()/n; r > 0; --r) {
            switch (dir) {
                case UP:
                    count_up(); break;
                case DOWN:
                    count_down(); break;
            }
        }
        dir = (rand() & (1<<b)) ? UP : DOWN;
        printf("%c|%c counter: %010d (runs=%d)\n",
                (counter < INT_MAX/2) ? '-' : '+',
                (char)dir, counter, ++runs);
    } while (counter != 0 && counter != INT_MAX);
    return 0;
}

--------

counter.h

/* Module Header
 * ============================================================================
 * Globally accessible non-overflow / non-underflow counter
 * ============================================================================
 * This file is to be included if that `counter` is used by some other module.
 * For more information see file `counter.c`.
 */
#ifndef COUNTER_H_
#define COUNTER_H_

int counter;
void count_up();
void count_down();
void reset();

#endif /* include guard */
 

---------

counter.c

/* Module Implementation
 * ============================================================================
 * Globally accessible non-overflow / non-underflow counter
 * ============================================================================
 * The variable `counter` is meant to be accessible from other modules if this
 * file is compiled to an object module. To MODIFY it only the three functions
 * provided here should be use. They make sure that once the lowest or highest
 * limit is reached no wrap-around will occur.
 */

#include "counter.h"
#include <limits.h>

int counter = 0;

void count_up() {
    if (counter == INT_MAX) {
        return;
    }
    ++counter;
}

void count_down() {
    if (counter == 0) {
        return;
    }
    --counter;
}

void reset() {
    counter = 0;
}
 

---------

Makefile

# !! Be sure indented lines below start with a TAB character (= ASCII \x09) !!
# (such are syntactically significant for `make` and not equivalent to spaces)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# un-comment the following line if your IDE by default builds a target `all`
#all: info
#     ^-- you may also want to change `info` to the prefered target to build
info:
    @echo 'use `make run`, `make tdd`, or `make cli` to compile/run a program'
    @echo '(furthermore `make clean` purges compiled objects and executables)'

CC=gcc -std=c89
.PHONY: all info run cli tdd clean 

run: subdirs programs/run-main
    @programs/run-main
tdd:
    @echo 'test driven development not supported'
cli:
    @echo 'no interactive cli-testing provided'
clean:
    rm -rf objects programs
    @rm -f a.out core

subdirs:
    @test -d programs || mkdir programs
    @test -d objects || mkdir objects

objects/run-main.o: main.c counter.h
    $(CC) -c main.c -o $@
objects/counter.o: counter.h
    $(CC) -c counter.c -o $@

programs/run-main: objects/run-main.o objects/counter.o
    $(CC) objects/run-main.o objects/counter.o -o $@

# EOF