C++Guns – RoboBlog blogging the bot

27.02.2017

C++ Guns - Auto Variables - Gute Beispiele

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

GotW 93 von Herb Sutter finde ich sehr gut. Es wird eine Reihe von Möglichkeiten für "auto" anhand von Beispielen gezeigt und erläutert. Das erleichtert das Programmieren wirklich sehr. Schon 1983 wurde "auto" von Bjarne Stroustrup in C++ implementiert. Aber dank dem schon damals schrottigem C musste er es wieder raus nehmen. Na, ich bin sehr froh post C++11 zu leben.

GotW 93

Es gibt auch eine Situation, in welche "auto" auf den ersten Blick ein falschen Typ ableitet. Aber bei genauerer Betrachtung ist es das alte Problem von unterschiedlichen integer Typen. Also unsigned long int + signed int = unsigned long int. Der unsigned long int Datentyp ist "stärker" als signed int. Diese Definition geht auf die Anfängen von C zurück. Und es gibt keine Lösung die alle Fälle abdeckt. Es können weder signed Werte in unsigned long dargestellt werden, noch passen unsignled long in signed int rein. Aber jedesmal auf under/overflow zu prüfen ist einfach nicht effizient genug. Auch "auto" wird dieses Problem nicht lösen. Aber es eliminiert die Folgefehler.

Wie erwähnt, ist der Ergebnistyp bei der Subtraktion von unsigned long und signed int wieder ein unsigned long, obwohl der Programmierer ein signed int als Ergebnis haben wollte. Ohne die Benutzung von "auto" ist das auch einfach zu erreichen. Aber es ist nicht ersichtlich, dass im Code ein versteckter Fehler lauert:


unsigned long x    = 42;
signed short  y    = 43;
int diff = x - y;

Also das ist das reinste Kudelmuddel. unsigned long und signed short gehn rein, und signed int kommt raus. Jetzt kann das Ergebnis wegen unsigned long aber größer sein, als ein int speichern kann. Und mit der Benutzung von "auto" ist der Ergebnistyp wieder unsigned long und kann damit keine negativen Werte speichen, obwohl das offensichtlich gewollt ist.

Als Ergebnistyp würde signed long am besten passen. Aber erklär das mal den Programmierer, und dokumentiere diesen Pitfall an 1000 Stellen im Code, so dass es auch in 30 Jahren noch nachvollziehbar ist: no way!
Andrei Alexandrescu Idee finde ich hier sehr schön. Wir lassen natürlich den Compiler die Arbeit für uns machen! Der Compiler hat schon die Information, dass der Ergebnistyp ein long sein muss. Das ein signed Typ gewollt ist, sagt der Programmieren. In Kombination mit C++11 std::make_signed, welches für jeden gegebenen integral Typ, einen signed integral Typ ausspuckt, lassen sich schön zwei Hilfsfunktionen erstellen.


// C++14 version, option 1
template auto as_signed  (T t){ return make_signed_t  (t); }
template auto as_unsigned(T t){ return make_unsigned_t(t); }

auto diff = as_signed(x - y);

Durch diesen Einzeiler wird der bestmögliche Typ abgeleitet und gleichzeitig spricht der Code, dass hier was besonderes mit der Arithmetik passiert.

/// \todo Beispiel Code der zeigt was mit const und referenzen passiert.

26.02.2017

Protected: Graph Cycle Schlange die sich selbst beisse

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

This content is password protected. To view it please enter your password below:

16.02.2017

C++ Guns - C++ ist kein object oriented sprache

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

Wie Arne Mertz schon in seinem Beitrag c-is-not-an-object-oriented-language/ sage, ist C++ keine Objekt orientierte Sprache. Ich möchte das aus meiner Sicht einmal verdeutlichen.
Eine Definition ob eine Sprache eine Objekt orientiere Sprache ist, wenn damit Objekt orientiert programmieren kann. Doch was ist ein Objekt?

Aus Objekt orientierter Sich ist ein Objekt eine Klasse, mit Funktionen und Attributen. Dabei kann sich das Verhalten des Objekts, also seine Funktionen, zur Laufzeit mittels Vererbung ändern (c++: virtual). Doch wann hört ein Objekt auf, ein Objekt zu sein? Das ändern des Verhaltens zur Laufzeit ist IMO keine Voraussetzung. Auch muss ein Objekt IMO keine Funktionen besitzen oder von anderen Klassen erben. Ein Objekt mit nur Attributen hat auch oft eine Entsprechung mit einem realen Objekt in der Welt. Zum Beispiel kann jeder Gegenstand mit Koordinaten xyz versehen werden. Dieses Koordinaten-Objekt selbst ist aber wieder aus drei einzelnen Objekten zusammengesetzt. Eben die Variablen, welche die x, y und z Koordinate speichern. Aus meiner Sicht ist schon eine einfache Variable, welche sogar direkt von einer spezifischen Hardware abhängig sein kann (size_t), schon ein Objekt.

So, C++ ist keine pure Objekt orientierte Sprache. Keine pure funktionale Sprache. Keine pure prozedural Sprache. Keine pure low level Sprache.
C++ ist eine multi paradigma Sprache. Und das ist auch gut so.
Für jede Aufgabe gibt es eine gute Lösung. Aber reale Welt Probleme bestehen aus vielen einzelnen Aufgaben die jede für sich eine gute Lösung besitzt. Wie kann dann überhaupt angenommen werden, dass eine Sprache, dir nur ein einzelnes, oder wenige Paradigmas unterstützt, überhaupt jemals echte Probleme gut lösen kann?

Kalt

Filed under: Allgemein — Thomas @ 07:02

Es ist kalt heute morgen.

kalt1

kalt2

12.02.2017

Rückschläge - Bahnhof in Nöten

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

Es gibt gute und schlechte Nachrichten. Erste die Guten:

Die Kopplung der Lichtschranken mit den Flipflops hat funktioniert. Ein provisorisch gelötetes Monoflop sorgt dafür, dass das dauerhafte Signal der Lichtschranke in einem kurzen Implus umgewandelt wurde, welches das FF triggerte. Bei der manuellen Bedienung wird kurz auf ein Taster gedrückt, bei der Automatik drückt ein Monoflop.
Als nächstes werde ich 6 Monoflops löten. Jeweils zwei für ein Signal. Und es kommen noch 5 Relais dazu, welche das Signal von der Lichtschranke invertieren. Momentan fällt das Relais ab, wenn der Zug kommt. Ich brauch aber ein das anzieht. (Und den 100k Ohm Widerstand nicht vergessen).

Und nun die Schlecht. Die Weiche am Bergbahnhof funktioniert überhaupt nicht zufrieden stellend. Ziel ist es ja die rote Pendelzuglok (Wie war nochmal der Name?) für die Pendelzugstrecke einzusetzen. Leider entsteht jedes mal ein Kurzschluss, wenn sie über die Weiche fährt. Auch der Einsatz eines kürzeren Schleifers brachte nur geringfügige Verbesserung. Nach genauem Hinsehen haben wir festgestellt, dass das Schleiferende das bewegliche, Zungenartige Teil der Weiche berührt. Obwohl er darüber hinweg gleiten sollte. Dummerweise hatten wir genau diese Weiche mit einem minimalen mechanischen Spannung eingebaut. Sie befindet sich an der Stelle, wo die Stärke der Steigung weniger wird und in eine Gerade übergeht. Diese Verformung führt dazu, dass der Kontakt sich ein wenig hebt.
Die erste Idee war: Wir schleifen das Ding mit dem Dremel einfach ein bisschen platt. Das hat auch gut funktioniert. Um den Kurzschluss aber komplett zu beseitigen, musste relativ viel geschliffen werden. Jedes mal wenn die Lok über die Stelle fährt, fällt sie wie in ein Schlagloch und hüpft da umher.
Es hilft alles nichts.
Um eine 100% funktionierende Automatik zu bekommen, müssen wir die Weiche mit den Abstellgleisen ausbauen, ersetzen, und spannungsfrei wieder einbauen. Dass dazu der halbe Bahnhof umgegraben werden muss, lässt sich nun mal nicht ändern.

Es ist schmerzlich wahr: BAUE NIEMALS WEICHEN IN STEIGUNGEN EIN!

Update:
Wir sind aber auch so blöd. Ein Stück Tesafilm auf die Stelle wo der Kurzschluss passiert hätte gereicht!

Lichtschranke - erste Experimente

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

Gestern war langer Eisenbahn Basteltag.
Ganze sieben Stunden haben wir gewerkelt, gelötet, gebohrt, geklebt, ja sogar gehämmert. Am Ende kam dann die erste funktionierende Lichtschranke heraus. Ein Relais fällt nun ab, wenn die Lok das Lichtschrankensignal unterbricht. Nun kann weitere Elektronik entworfen werden, um die Lichtschranken mit den vorhandenen Flipflop und Monoflops zu verbinden.

lichtschrankenelektronik

Die fünf Lichtschranken Platinen. Jede ist an die Stromversorgung angeschlossen. Der Anschluss für die Sender Infrarot LED haben wir auf eine extra Platine mit Schraubklemmen gezogen. Auf dieser Platine befinden sich noch vier Fahrstromrelais für die Bergbahn mit Schraubklemmen für die Anschlussgleisstücke.

lichtschranke1

In der Lücke zwischen Lok und Waggon befindet sich der unsichtbare Lichtstrahl der LEDs, die wir geschickt in kleine gemauerten Häuschen versteckten. Die Häuschen stehen genau da, wo die Lok zum stehen kommen soll.

lichtschranke2

Die gemauerten Werke passen gut in die Umgebung. Dass sie ein wenig schief stehen ist kein Problem. Der Empfänger ist dank Operationsverstärker empfindlich genug.
Es fehlt noch ein wenig Streusel an der Seite. Vielleicht ein paar graue Schottersteine? Das ist immerhin ein Bahnbetriebsbauwerk und darf nicht von Gestrüpp zugewuchert werden.

06.02.2017

C++ Guns - call by reference; return by auto

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

Es gibt viele Arten Daten in eine Funktion hinein zu stecken und auch wieder heraus zu bekommen. Hier liste ich die Vor- und Nachteile auf.
Hinein ist einfach:

Call by reference:

Sofern es sich um stink normale Daten handelt, ist das die bevorzugte Methode. Nicht stink normale sind z.B. Funktionen/-Objekte.


void callByRef(const Point3D& point);

Eine Referenze belegt dabei genauso viel Platz wie ein Pointer, also 8byte. Hat man also nur ein int/real zu kopieren, lohnt es sich nicht. Bei so kleinen Objekten ist call by value genauso gut.

Call by value:
Bevorzugt für kleine Objekte oder wenn explizit eine Kopie gebraucht wird. Auch für Funktionsobjekte, Lambdas, Iteratoren etc. Da call by value immer eine Kopie erzeugt, sollt es nicht für große Objekte verwendet werden.


template
void callByValue(const int x, int float, Iterator iter, Function pow, Rand rand);

Call by pointer:
Nee braucht man nicht. Alle sinnvolle Fälle sind schon durch reference und value abgedeckt. Ausserdem, schonmal versucht ein const Pointer auf ein const Objekt zu machen? Hässlich.


void callByPointer(Data const * const data) {
 *data = x; // BUMM
 data++; // BUMM
}

Heraus ist schwer:
Return by reference:
So etwas macht nur Sinn, wenn die Funktion eine Memberfunktion einer Klasse ist. z.B: std::vector::at(). Das Objekt, auf das die Referenz zeigt, muss ja noch weiter existieren, nachdem die Funktion wieder verlassen wurde. Aber normale Variablen in Funktionen existieren nur bis zum Funktionsende.

Return by value:
Ich nehme an, dass ist der normale Fall. Für einfache Typen (int, real) ist das auch kein Problem. Für komplexe Datentypen mit move Konstructor ist es auch kein Problem. z.B.


auto returnByValue() { return std::vector x;}
std::vector = returnByValue();

Ansonsten kann noch die Compiler Optimierung zuschlagen und einem retten. Aber seit dem man moven kann ist das alles kein Ding mehr.

Return by auto:
Mehrere Variablen zurück geben ist kein Problem.


auto returnByAuto() { return make_pair(x, true); }
auto [x, ok] = returnByAuto();

Structured Bindings nennt sich das.

Return by pointer:
Komm, spar's dir.

03.02.2017

C++Guns - Pointer of Pointer of ...

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

Was ist ein Pointer auf ein Pointer auf ein Pointer u.s.w. und wozu braucht man das?
Man braucht es eigentlich nicht. Was es ist, werde ich versuchen hier zu erklären.
Ein Pointer verweist auf ein anderes Objekt. Und dieses Objekt kann mit Hilfe des Pointers manipuliert werden.
Dazu ein kleines Beispiel:


void manipulate(int *x) { *x = 42; }
int x = 2;
manipulate(&a);
cout << a; // print 42

Das Objekt, auf welches der Pointer zeigt, kann auch nachträglich geändert werden. Beispiel


int a=2;
int* p = &a;
cout << *p; // print 2

int b=3;
p = &b;
cout << *p; // print 3

Es ist auch möglich Pointer als Funktionsargument zu nutzen und innerhalb der Funktion den Pointer auf ein anderes Objekt verweisen zu lassen:


int b=42;
void manipulatePointer1(int* p1) {
    p1 = &b;
}
 
int a=2;
int* p = &a;
cout << *p; // print 2

manipulatePointer1(p);
cout << *p; // should print 42, but print 2

So funktioniert es aber nicht. Die Subroutine manipulate1 ändert, worauf der Pointer p1 zeigt. Aber nicht worauf der Pointer p in der aufrufende Routine zeigt. Um das zu erreichen, muss die Adresse des Pointer übergeben werden, statt nur seinen Wert (die Adresse von a). C++ ist eben einen call-by-value Sprache.


int b=42;
void manipulatePointer2(int** p) {
    *p = &b;
}

manipulatePointer2(&p);
cout << *p; // print 42!

Alles klar?

Das Spiel kann man noch weiter treiben und den Pointer durch 3, 4 Funktionen schleusen. Es braucht dafür aber niemals mehr als ein Pointer auf ein Pointer. Da die Adresse des ersten Pointers p, in die Funktionen übergeben wird, und nur diese Adresse wird gebraucht.
Also ein Pointer auf ein Pointer auf ein Pointer ist ein kranker Scheiß, den man nicht nutzen soll.

23.01.2017

Mehr Gedanken zur Automatik

Filed under: Allgemein — Tags: , — Thomas @ 14:01

Da wären wir schon bei der nächsten Idee zur Automatik. Die Automatik unterteilt sich also in drei Module: Fahrstrassensicherung, Halbautomatik, Vollautomatik.
Hier die einzelnen Aufgaben:
Die Fahrstrassensicherung hat zur Aufgabe, dass erst alle Weichen in der richtigen Position, alle beteiligen Signale auf Halt, und alle Züge die Blockstrecke verlassen haben, bevor das gewünschte Signal auf Fahrt geschaltet werden kann. Dies ist einfach durch eine Reihe von Relais zu realisieren, die alle hintereinander geschaltet sind.
So ergibt sich eine Abhänigkeit, welche Weichen/Signale von welchen Weichen/Signale/Blockstrecken abhängig sind.

In der Halbautomatik ist eingelötet, WELCHE Weichen/Signale geschaltet werden müssen, um eine Fahrstrasse zu erstellen. Die Aufgabe ist ähnlich der Fahrstrassensicherung, aber die Implementierung ist anders. Hier nochmal die genauen Unterschiede: Die Fahrstrassensicherung leitet ein Signal zum Umschalten einer Weiche/Signal erst dann durch, wenn alle anderen Weichen/Signale in der vorgegebenen Position sind. Und die Halbautomatik gibt das Signal, dass jetzt diverse Weichen/Signale umschalten sollen.
Zum besseren Verständnis ein Beispiel:
Zug auf Gleis 2 soll los fahren (also der Mensch drückt die Taste "Signal2 Fahrt"). Bevor das Signal wirklich umspringt, müssen ja folgende Bedingungen erfüllt sein:
1) Die Ausfahrtweiche muss auf Gleis2 umspringen.
2) Das Signal auf Gleis 1 muss auf Halt stehen
3) Die Blockstrecke hinter dem Bahnhof muss frei sein.

Damit die Weiche umschaltet, müssen folgende Bedingungen erfüllt sein:
4) Das Signal auf Gleis 1 muss auf Halt stehen.
5) Das Signal auf Gleis 2 muss auf Halt stehen.
6) Die Blockstrecke hinter dem Bahnhof muss frei sein.

Einige Bedingungen wie z.B. 2) und 4) kommen doppelt vor. Das wird später Optimiert. Momentan geht es nur um das Verständnis.
Was sind also die genauen Aufgaben der Fahrstrassensicherung und der Halbautomatik?
Für die Halbautomatik gilt:
Benutzer sagt Signal Gleis 2 Fahrt, dann Weiche auf Gleis 2 schalten und Signal Gleis 1 auf Halt.
Aber es passiert noch nichts, erst kommt die Fahrstrassensicherung.
Das Signal auf Gleis 1 kann eigentlich sofort auf Halt gesetzt werden. Wir haben soweit ich das beurteilen kann, keine Möglichkeit festzustellen, "dass ein Zug komplett aus dem Bahnhof ausgefahren ist".
Für die Weiche sieht es anders aus. Beide Signale stehen auf Halt. Und ein paar Sekunden später ist der Zug auch aus der Blockstrecke rausgefahren. Nun sind alle Bedingungen erfuellt und das Signal an die Weiche weitergeleitet. Diese Springt um. Damit sind auch alle Bedingungen für das Signal auf Gleis 2 erfüllt und es springt um auf Fahrt.

21.01.2017

Idee: Halbautomatik

Filed under: Allgemein — Tags: , — Thomas @ 11:01

Mir kam gerade eine recht witzige Idee: Eine Halbautomatik.
Es gibt ja einige Schaltvorgänge, die müssen immer zusammen ausgeführt werden. Zum Beispiel dreht ein Zug auf Gleis 1 seine Runden. Nun soll der Zug auf Gleis 2 los fahren. Was ist im Prinzip zu tun?
Signal Gleis 1 Halt.
Weiche (Ausfahrt) auf Gleis 2 umschalten.
Signal Gleis 2 Fahrt.

Und äquivalent, wenn der Zug auf Gleis 1 losfahren soll:
Signal Gleis 2 Halt.
Weiche auf Gleis 1 umschalten.
Signal Gleis 1 Fahrt.

Es gibt also 2 Sachen die immer passieren:
Die Weiche schaltet immer auf das Gleis mit dem Signal welches Fahrt zeigt. Beide Signale sind niemals gleichzeitig grün.

Also für die Halbautomatik gilt:
Wird ein Signal auf Fahrt geschaltet, wird automatisch das andere Signal auf Halt gestellt und die Weiche gestellt.

Dank der modularen Bauweise ist das super einfach zu realisieren. Es bedarf im Grunde nur ein paar Drahtbrücken auf einer extra Platine. Ich bin gespannt ob damit die Arbeit beim Gleisbildstellwerk vereinfacht wird.

Update 1:
Also die Idee ist gut. Das macht schon ordentlich was her, wenn die Weichen automatisch springen, nachdem man das Signal betätigt hat. Aber leider hat es nicht ganz so funktioniert wie geplant. Mir war nicht bewusst, dass die Tasterleitung, die zu den Flipflops geht, nach Masse geschaltet werden, nachdem das FF betätigt wurde. Das ist wohl ein Resultat der Taster/Relais/FF Verriegelung. Aber es ist nicht so schlimm. Die endgültige Implementierung der Halbautomatik muss sowieso mit der automatischen Fahrstrassensicherung und der eigentlichen Automatik dann abgestimmt werden.

Weiter gehts hier

« Newer PostsOlder Posts »

Powered by WordPress