C++Guns – RoboBlog blogging the bot

28.02.2017

C++ Guns - type traits Beispiel

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

Hm templates sind zwar toll und so, aber ein template Typ kann ALLES sein. Da ist Polen offen, wie mein Chef sagt. Ist schon komisch. Programmiert man feste Typen ein, sind sie nicht variabel genug. Nimmt man templates, sind sie zu variabel. Im Endeffekt kommt es doch aber nur auf die Eigenschaft des Typs an. Die meisten Algorithmen die integer nutzen, scheren sich nicht darum ob das nun ein short oder long Type ist. Einfache oder doppelte Zahlengenauigkeit - egal. Ein 2D Linien Type von Qt, Boost oder meinen eigenen - egal. Wichtig ist doch nur, was der Type kann, und nicht wie er im Detail aufgebaut ist.
Also muss für jeden template Parameter auch noch deren Eigenschaften im Programm mitgeteilt werden. type_traits macht genau das.
Hier ein kleines Beispiel von gerade eben.


template<class T>
struct is_line : std::false_type {
};

template<>
struct is_line<QLineF> : std::true_type {
};

template<class Container>
void func(const Container& lines) {
  static_assert(is_line<typename Container::value_type>::value, "Expect a geometric line type for Container::value_type");
  for(const auto& line : lines) {
    ...
  }
}

Und so schaut eine mögliche Fehlermeldung dann aus, wenn die Funktion mit nicht line-Typen aufgerufen wird.

error: static assertion failed: Expect a geometric line type for Container::value_type

Hint: Das keyword "typename" wird gebraucht, weil der Type "Container" ein template Type und gleichzeitig "Container::value_type>" auch wieder von einem template Type kommt.

27.02.2017

Protected: Was ich bin

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

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

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.

Powered by WordPress