War normal lecker, dafür witzig.
08.03.2017
Sushi - Eigen
05.03.2017
TODO C++ Guns - Vererbung und Datenstrukturen und Performance
Ich denke, Vererbung und Datenstrukturen sind nicht gut für die Performance.
Mir ist auch noch kein so wirklicher Anwendungsfall in der Praxis begegnet. Heute habe ich mal was probiert.
Ein Stück Kanal, welches in der Erde liegt und Abwasser durch schwimmt, nenne ich von nun an eine Haltung. Eine Haltung hat mehrere Attribute, die für eine Kanalnetzberechnung von Bedeutung sind. Z.B. Nummer, Länge, Breite, Profil, Rauigkeitswert, Zulauf und Ablauf Haltungen. Diese Variablen müssen für die Performance nah im RAM beieinander liegen. Es sollten also keine Variablen im Datentype existieren, die nicht für die Berechnugn relevant sind. Aber dennoch hat eine Variable weitere Attribute, wie z.B. den Straßennamen, Haltungsbezeichner, Schachtbezeichner und viele mehr.
Es ergeben sich also zwei getrennte Datentypen die beide eine Haltung darstellen. Das ist verwirrend. Ich kann natürlich einen normalen Haltungsdatentyp erstellen mit allen Attribute und einen Performance Haltungsdatentyp mit nur wenigen wichtigen Attribute. Aber das ist auch eine Art doppelter Code.
Also versuchen wir es mal mit Vererbung. Der Datentyp Haltung erbt vom Datentype HaltungPerformance.
struct HaltungPerformance {
int NR = 0;
double laenge = 0;
int B = 0;
int profilKennziffer = 0;
int ZU[3];
int AB[2];
int KB = 1;
};
struct Haltung : public HaltungPerformance
{
string strassenNamen;
string bezeichner;
string schachtBezOben, schachtBezUnten;
};
class DataBase {
public:
private:
std::vector<Haltung> haltungen;
}
Das Befüllen der Datenstruktur und das normale Arbeiten mit Haltungen ist einfach. Das ein Teil der Haltung in einem zweite Klasse ausgelagert ist, ist hier transparent und für den Programmierer nicht sichtbar.
Aber wie kann mit der performanten Haltungs- Klasse die Berechnung durchgeführt werden? Eine Möglichkeit ist es, eine Kopie der HaltungPerformance Daten zu machen und in ein extra vector zu speichern.
std::vector<HaltungPerformance> haltungenRechnen(begin(haltungen), end(haltungen));
Nunja, vorher hatten wir doppelten Code, nun haben wir doppelte Daten. Gibt es noch einen anderen Weg?
Versuchen wir es mal nicht mit Vererbung, sondern mit Composition? (Sagt man das so?)
Lichtschranken Verkabelung
Heute war ein schweißtreibender Arbeitstag. Drei der geplanten fünf Lichtschranken sind montiert. Jede Lichtschranke braucht immerhin fünf Kabel plus zwei Abschirmungen. Wir verlegen Sender und Empfänger in getrennte Leitungen, um die gegenseitige Störung zu vermeiden. Dazu braucht es auch Steckverbinder und die Kabel mussten quer durch die Anlage gelegt werden. Das erfordert viel Über-Kopf-Arbeiten und Improvisation. Einige Kabel mussten wir auf der Rückseite von außen verlegen, da in den Kabelkanälen unter der Anlage einfach kein Platz mehr ist.
Nächste Woche können dann die Kabel von der Anlage zum Stellpult verlegt und angeschlossen werden. Hoffentlich funktioniert auch alles dann. Getestet haben wir die Lichtschranke jedenfalls ausgiebig.
Nächste Woche gibt's Bilder von den Silberlingen ;)
03.03.2017
Lichtschranken Fortschritte
Es gibt eine neue Lichtschranke auf der Anlage. Oben beim Bergbahnhof Gleich 1. Wurde eben erst frisch ein betoniert. Daher ist auch noch kein Gras über den Leim gewachsen ;) Und es liegt auch noch Baumaterial auf dem Gleis, dass schnellst möglichst weggeräumt werden muss! Dies ist also die Position am Gleis, an der die Lok stehen bleiben soll. Bzw. erst bremsen und dann stehen bleiben. Viel Raum bis zum Prellbock ist nicht mehr. Aber auf Gleis 2 ist die Sachlage noch schlimmer. Wird schon.
Die Bäume im Hintergrund geben einen tollen Blickfang und Begrenzung zur Wand hin. Und es muss auch Sorge getragen werden, dass das Bächlein nicht die Gleisen unterspült.
Ein idyllisches Plätzchen zum verweilen. Frisches Bergwasser. Und sogar, ich glaube, ein Apfelbaum?
Die Bevölkerung auf dem Berg möchte nicht nur ins Tal transportiert werden, auch müssen Kohlen nach oben geschafft werden. Dazu eignet sich diese blaue Rangierlok hervorragend. Oben wartet schon der leere Waggon. Doch die Weiche muss noch umgestellt werden. Schnell!
C++ Guns - Fun with enable_if
Schonmal std::enable_if benutzt? Ich nicht. Man kann damit Funktionen deaktivieren... Irgendwie... SFINAE... Gesundheit!
Die Beispiele dazu sind alles nur Kraut und Rüben. Vor lauter Template Scheiße sieht man den Kern der Aussage nicht.
Also, hier mein eigenen Testfall:
// Template Parameter N wird einfach an die Funktionen weitergegeben
template<int N>
class Testclass {
public:
// Um enable_if und SFINAE zu nutzen, muss die Funktion ein Template sein.
// Dummerweise muss der Template Parameter afaik auch in enable_if genutzt werden.
template<int U=N>
enable_if_t< U>=1 , int>
eins() {
return 1;
}
// Wo steckt der Rueckgabetyp? Im zweiten enable_if Template Paramter. Strange...
template<int U=N>
enable_if_t< U>=2 , int>
zwei() {
return 2;
}
// Dabei darauf achten, dass die beiden dreioverload() Funktionen auch unterschiedliche Template Typen
// haben. Sonst ist, wie beim normalen function overload, kein ueberladen moeglich. Wo ist der Unterschied
// in den Typen? N ist gleich. U ist gleich. int ist gleich. Nur der erste Parameter von enable_if evaluiert
// einmal zu true und einmal zu false. Das sind zwei unterschiedliche Typen!
template<int U=N>
enable_if_t< U>=3 , int>
dreioverload() {
return 3;
}
template<int U=N>
enable_if_t< !(U>=3) , int>
dreioverload() {
return -1;
}
};
cout << Testclass<1>().eins() << "\n";
cout << Testclass<2>().eins() << "\n";
cout << Testclass<2>().zwei() << "\n";
cout << Testclass<2>().dreioverload() << "\n"; // Print -1
Mal abwarten ob so etwas im normalen Code sinnvoll einsetzbar ist.
01.03.2017
todo: owning and non owning
Jeder Funktionsparemter der (const)(Referenz)(Pointer) ist, ist non-owning
C++ Guns - Point3D ist KEINE Erweiterung von Point2D!
Point3D ist KEINE Erweiterung von Point2D!
Sondern Point2D ist eine Reduzierung von Point3D. WTF? Ja ist so :P
Beweis:
struct Point2D {
double x,y;
};
struct Point3D : public Point2D {
double z;
};
static_assert(std::is_standard_layout<Point3D>::value);
main.cpp:14:1: error: static assertion failed static_assert(std::is_standard_layout<Point3D>::value);
In der C++ Sprache ist alles, was berechnet werden kann, ausdrückbar. Wird dabei das Standard Layout nicht eingehalten, ist definitiv der falsche Weg eingeschlagen worden.
Der zweite Tipp ist, dass ohne Standardlayout die Typen auch nicht POD sind. Und man kann wohl getrost annehmen, wenn ein double POD ist. Dann muss auch ein Type mit 2 oder 3 double POD sein!
Lösung:
Point2D ist auch im Point3D enthalten. Es wird halt nicht x,y,z beachtet, sondern nur x,y.
Wenn PointND als Array implementiert wird, ist das nichts anderes als array_view (string_view).
Sprich, wird der Datensatz als Point3D gespeichert (owning), sind Point2D und Point1D non-owning.
Beispiel:
template<size_t N>
struct PointNDRef : public array_view<double, N>
{
using array_view<double,N>::array_view;
};
template<size_t N>
struct PointND : public std::array<double,N>
{
operator PointNDRef<2>() {
return this->data();
}
operator PointNDRef<1>() {
return this->data();
}
};
static_assert(std::is_standard_layout<PointND>::value);
using Point3D = PointND<3>;
using Point2DRef = PointNDRef<2>;
void func(const Point2DRef p) {
p[0];
get<0>(p);
}
int main() {
Point3D p;
func(p);
return 0;
}
Die Implementation von array_view
todo: dereference of a null pointer at compile time?
28.02.2017
C++ Guns - type traits Beispiel
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.




