C++Guns – RoboBlog blogging the bot

05.02.2018

C++ Guns: Generic Data Type Design Pattern - Teil 4

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

Die Idee, für User defined Datentypen jede Variable in einem std::tuple oder std::array zu halten, kann nicht überall gut sein. Für jede Variable benötigt man noch eine getter Funktion um der Variablen einen Namen und damit Semantik zu geben. Ich hatte in den letzten Kapitel noch erwähnt, dass es viel Sinn macht, diese Funktionen als const zu deklarieren. Da in den Beispiel Datentypen jede Variablen von der anderen abhängt. Und daher dürfen sie nicht beliebig getrennt verändert werden.

Wann diese Regel nicht mehr gilt ist ganz klar: Wenn Variablen in einem Typ gespeichert werden, die nicht voneinander abhängen. Da frage ich mich natürlich gleich, warum sie dann in einem gemeinsamen Typ gespeichert sind. Aber womöglich ist dieser Übergang ja fließend.

Sowas wie PointReader/Writer hätte als Membervariable natürlich den Path zur Datei, bzw. irgendeine Art von Device/File Handle. Eventuell noch eine Error Variable. Diese beiden Variablen sind natürlich nicht so streng gekoppelt wie xyz, aber gehören dennoch zusammen.

Eigentlich sind das ja Klassen die Verhalten simulieren. Sowas wie ein Widget. Mit Fenstergröße, Position, Farbe, Childwidgets.

Also ich halte mal fest, für Membervaribalen std::tuple std::array nutzen erscheint mir für Massendaten sinnvoll. Sonst nicht. Mal sehn ob sich das in der Praxis bewährt.

02.02.2018

C++ Guns: Generic Data Type Design Pattern - Teil 3

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

Im letzten Teil hatte ich das serialisieren angesprochen wie das z.B. mit der Qt Klasse (und andere) QDataStream möglich ist.

A data stream is a binary stream of encoded information which is 100% independent of the host computer's operating system, CPU or byte order. For example, a data stream that is written by a PC under Windows can be read by a Sun SPARC running Solaris.

Da nach dem angesprochenen Entwurfsmuster jeder Datentyp von std::array oder std::tuple erbst, braucht es auch nur eine Serialisierungsfunktion für alle User defined Typen. Es fehlt nur eine Möglichkeit den Klassennamen automatisiert anzugeben. So ein bissel C++ reflektion fehlt leider, aber es sollte kein Problem sein den Klassennamen selbst hin zu schreiben,

strcut UserType : public std::tuple<int, double, std::string>
{
  static const char* classname = "UserType";
}

So hätte man eine Möglichkeit, User defined Datentypen automatisiert zu verarbeiten, da sie alle das selbe Interface haben. (Vom Typ std::array oder std::tuple sind).
Ein netter Gedanke.
Aber ich schweife etwas ab. Im vierten Teil versuche ich ein Anwendungsfall zu finden, bei dem dieses Muster nicht mehr passt.

C++ Guns: Generic Data Type Design Pattern - Teil 2

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

Weiter gehts mit der Anwendung meiner Idee alle einfachen Datentypen als std::array oder std::tuple abzubilden. Wie im ersten Teil schon für den Point2D Datentyp realisiert.

Jetzt werde ich einen Range Datentyp bauen. Dieser besteht einfach aus zwei Zahlen die eine obere und untere Grenze von etwas angibt. Zusätzlich ist die Spanne zwischen obere und untere Grenze interessant. Dies ist aber eine abgeleitete Größe und darf deshalb nicht als gespeichert werden, sondern soll extra berechnet werden.

struct Range : public std::array<double,2>
{
  const auto& lower() const {
    return operator[](0);
  }

  const auto& upper() const {
    return operator[](1);
  }

  const auto size() const {
    return upper()-lower();
  }
}

So, die Intitialisierung erfolgt über eine Liste. Der Zugriff einheitlich über Funktionen. Sowohl für gespeicherte Werte sowie für abgeleitete. Das ist schon mal gut.

Hässliche Member Variablen fallen mit diesem Entwurfsmuster weg. Damit muss man sich und auch nicht mehr überlegen wie man sie nennt. Mit Unterstrich, Groß/Kleinschreibung oder ganz kryptisch und verkrüppelt. Nein, die Entscheidung muss überhaupt nicht mehr gefällt werden.

Da dieses Beispiel doch recht einfach war, versuchen wir es doch mal mit den Stochastischen Momenten.
Also einen Datentyp welche Mittelwert, Varianz und Standardabweichung bereitstellt. Nun kann man aus einer vorher erstellten Summe und der Anzahl der Summanden diese drei Werte immer berechnen. Aber das erscheint mir zu aufwendig und irgendwie nicht richtig. Daher tendiere ich dazu, avg var und std direkt in dem Datentyp abzuspeichern.

Versuchen wir es:

struct StochastikMoment : public std::array<3,double>
{
  template<typename Container>
  StochastikMoment(const Container& data) {
    operator[0] = sum(data)/data.size();
    operator[1] = ...;
    operator[2] = std::sqrt(operator[1]);
  }

  const auto& avg() const {
    return operator[0];
  }

  const auto& var() const {
    return operator[1];
  }

  const auto& std() const {
    return operator[2];
  }
}

Die Initialisierung kann wieder über eine Liste erfolgen, wenn nötig. Aber im Normalfall werden die Momente aus vorgegebenen Daten berechnet. Den Algorithmus dafür spare ich mir.
Der Zugriff Erfolg wie gewohnt über Funktionen. Ich denke an diesem Beispiel wird klar, warum nur const Funktionen in diesem Entwurfsmuster Sinn machen. Die Standardabweichung ist streng verbunden mit der Varianz. Und die Varianz hängt stark vom Mittelwert ab. Es macht einfach keinen Sinn, Mittelwert und Varianz algorithmus zu bestimmen aber die Varianz manuell vorzugegen. Diese drei Werte hängen voneinander ab und treten dementsprechend immer zusammen auf.

Hätte ich mich bei diesem Datentyp anders entschieden und die drei Werte immer wieder berechnet, und die Summe gespeichert, dann wäre das doch auch ganz klar. Wenn man den Mittelwert berechnet, kann man ihn nicht über avg() Setzen.

Ich frage mich ob diese Art und Weise allgemeingültig für alle einfachen Datentypen ist.

Auf jeden Fall ist das serialisieren dadurch einfach. Weiter im dritten Teil.

C++ Guns: Generic Data Type Design Pattern - Teil 1

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

Hmm das Problem lässt sich vermutlich am besten anhand von Beispielen erklären.

Wie immer nehm ich gern mein Point2D Datentyp her. Es gibt ja verschiedene Wege so etwas zu implementieren.
Eine class mit privaten x/y Variablen und entsprechende getter/setter. Oder ein struct mit nur public x/y Variablen:

strcut Point2D {
 double x, y;
}

Das Problem bei dieser Implementierung ist, dass die x/y Variablen nicht in einem Array sind. Das mag vllt. auf den ersten Blick unnötigt erscheinen, aber im laufe der Jahre habe ich den Zugriff auf xyz anhand von Indizes 1,2,3 öfter mal gebraucht. Die Alternative wäre also:

strcut Point2D {
  std::array<double,2> xy;
}

Aber hier fehlt dann die Möglichkeit gut auszudrücken, dass man explizit auf die x-Variable zugreifen möchte. Wie schnell hat man doch die Index Zahlen verdreht. Eine Kombination beider Variablen ist auch Möglich:

strcut Point2D {
  std::array<double,2> xy;

  auto& x() {
    return operator[](0);
  }

  auto& y() {
    return operator[](1);
  }
}

Die const Varianten der x() y() habe ich jetzt mal weg gelassen. Ich möchte jetzt auf etwas anderes hinweisen: Wie sieht es aus wenn man dieses Konstrukt in seinem Code nutzt:

  Point2D p;
  p[0] = 10;
  p[1] = 20
  cout << p.x() << p.y();
  p.xy[0] = 100;
  p.xy[1] = 200;
  p.xy = {3,4}; ???
  cout << p. xy; ???
  p.x() = 1000;
  p.y() = 2000;

Also ganz ehrlich: Nein. Das ist zu viel des Guten. Zu viele Freiheitsgrade. Das verwirrt doch nur. Und wer die Qual der Wahl hat, trifft die falsche Entscheidung. Vor allem, muss man wirklich die Koordinaten über die Funktion x() und y() setzten können? Das sieht auch ungewohnt aus. Und die extra Variable xy erscheint auch unnötig und überflüssig. Zugriff auf Member, über Funktionen, über Index Operator. Uneinheitlich!

Vor ein paar Monaten bin ich ja auf noch eine weitere Möglichkeit gestoßen, diesen Datentyp zu implementieren. Siehe C++ Guns - C++17 Extension to aggregate initialization. Und ich muss sagen, so schlecht ist das gar nicht. Es bietet eine Menge.

Die Idee ist, den Point2D Datentyp von einem std::array erben zu lassen. Und für die bessere Lesbarkeit noch getter Funktionen mit guten Namen bereit zu stellen. Durch die Vererbung IST der Point2D Datentyp ein Array. Und bietet automatisch den Index Zugriff an. Und durch die getter Funktionen bekommen die einzelnen Arraypositionen eine Semantik.

strcut Point2D : public std::array<double,2>
{
  const auto& x() const {
    return operator[](0);
  }

  const auto& y() const {
    return operator[](1);
  }
}

Nun gestaltet sich die Nutzung schon einfacher. Das Setzten der Werte passiert über eine Initialisierungsliste. Das Lesen über Funktionen. Und in den Fällen wo ein Indexzugriff besser ist, ist das nun auch realisierbar.

  Point2D p {1,2};
  cout << p.x() << p.y();
  p[0] = p[1];

Hier habe explicit nur const getter Benutzt. Dazu sage ich später noch mehr.

Im nächsten Teil werde ich Versuchen, dieses Muster auf andere Datentypen anzuwenden.

23.01.2018

C++ Guns - C++17 class template argument deduction user guide

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

Es ist ja bekannt, dass der Compiler die Template Parameter von Funktionen selbst bestimmen kann.

template<typename T>
T func(T val) {
  return val;
}

auto i = func(1); // i int

Seit C++17 funktioniert das nun auch mit Klassen. Das Feature hierfür nennt sich __cpp_deduction_guides und wird seit GCC 7 unterstützt.
https://en.cppreference.com/w/cpp/language/class_template_argument_deduction
Mehr noch, es können User Guides angegeben werden. Mit denen wird festgelegt, wie genau sich der Type der Klasse ableiten soll.
Hier ein einfaches Beispiel:

#include <type_traits>

#ifndef __cpp_deduction_guides
#error "need gcc 7"
#endif

template<class T>
struct A {
    using value_type = T;

    template<typename U, typename V>
    constexpr A(U, V) {
    }
};

// Das ist der  class template argument deduction user guide
// Der gemeinsame Type von U und V soll T von der Klasse A sein.
template<typename U, typename V>
A(U, V) -> A<std::common_type_t<U, V>>;


constexpr A x(1, 2.0);
// Der gemeinsame Type von int und double ist double
// Zur Compilezeit getestet!
static_assert(std::is_same_v<decltype(x)::value_type, double>);
// yeah

Sehr geiles feature.

19.01.2018

GIT specify private ssh key

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

Das dumme an private keys ist, man kann sie nicht so einfach wie Passwörter überall mit hin nehmen. Schon garnicht zu anderen Rechner wo man selbst kein eigenen Useraccount hat. Und der vorhandene trusted user sein eigenen ssh key hat.
Bei ssh kann man leicht sein eigenes keyfile mit angeben. Bei GIT geht das auch, nur anders:

$ GIT_SSH_COMMAND='ssh -i key' git pull

Nur solle man tunlichts nicht vergessen sein keyfile wieder zu löschen. Sonst hat das ganze kein Sinn ;)

16.01.2018

C++ Guns: passing lambda to class - class template argument deduction

Filed under: Allgemein — Tags: — Thomas @ 12:01

auf http://en.cppreference.com/w/cpp/language/class_template_argument_deduction
gibts das halbe beispiel

// deduces Foo<T>, where T is the unique lambda type
 std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...})); 

Ein compilierfähiges Beispiel wäre folgendes:

template<typename T>
struct Foo {
        Foo(T lambda) : lambda(lambda) {
        }


        void operator()(int y) {
                lambda(y);
        }

        T lambda;
};

int main() {
        std::vector<int> vi {1,2,3};
        std::for_each(vi.begin(), vi.end(), Foo( [&](int i) { std::cout << i << "\n";} ));
}

g++ --version
g++ (GCC) 7.1.0
g++ -std=c++17 lambdaFoo.cpp
$ ./a.out
1
2
3

Aber hat irgendjemand eine Idee, was man damit anfangen soll? Mir erschließt sich das gerade nicht.

11.01.2018

C++ Guns: C++ && Unicode

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

Vorheriger Post Qt && Unicode

Heute möchste ich Unicode Zeichen mit C++ schreiben. Und zwar mit dem normalen 1 Byte std::string und std::ostream, also std::cout. Mit Qt war das einfach, wie im vorherigen Post zu lesen ist. Die Klasse QString nutz intern 2 Byte Zeichen und sonst kümmert sich Qt ja auch um alles.

Mit std C++ ist es eigentlich auch ganz einfach. Man darf sich nur nicht durch UTF8, UTF16, UTF32, Raw Strings, Kodierungen und Locale verwirren lassen.

Das Problem ist einfach gesagt, dass es viel mehr Zeichen auf der Welt gibt, als mit einem normalen, klassichen 1 Byte char dargestellt werden kann. Da passen nur 256 unterscheidliche Zeichen rein. Von denen werden für ASCII noch viel weniger benutzt. Um mehr Zeichen darzustellen, nutzt man einfach einen größeren Char Type. Da gibt es den wide char wchar_t, der aber nicht standardisiert ist und unter Windows und Linux wohl unterschiedlich viele Byte gelegt.

Die standardisierten Typen lauten char16_t und char32_t mit 2 und 4 Byte. Mit dem 4 Byte Char lassen sich ca. 2 bzw 4 Milliarden unterschiedliche Zeichen darstellen! Das sollte dann wohl reichen.
Leider bezahlt man dafür mit doppelten bzw. vierfachen Speicherplatzverbrauch, was für manche Anwendungen unmöglich ist. Noch dazu möchte man auch kompatibel zu anderen Code sein, der komplett auf std::string setzt. Und das ständige Unterscheiden zwischen std::string, std::wstring, std::u16string und std::u32string ist auch unmöglich.

Also hat man sich überlegt, ob nicht die gebräuchlichsten UTF-32 Zeichen als 1- oder 2 Byte gespeichert werden kann. So ist dann wohl UTF-16 und UTF-8 entstanden.
Konkret bedeutet dies, dass UTF-8 eine Kodierung mit variabler Breite ist. Häufig vorkommende Zeichen passen in 1 Byte, weniger oft verwendete Zeichen in 2 Byte und seltene Zeichen in 3 oder 4 Byte.

Damit ist, bist auf Umlaute, wieder alles gut. Umlaute müssen in UTF-8 als zwei 1 Byte Zeichen kodiert werden. Damit besteht der UTF-8 String u8"L\u00e4cheln!" zwar aus ach Unicode-Zeichen, aber da das zweite Zeichen ein kleines ä ist, welches in UTF-8 als zwei Bytes, nämlich 0xc3a4 kodiert ist, ist der komplette String 9 Zeichen lang!

Dies führt nun zu bestimmten Problemen, wenn Strings nach ihrer Länge ausgewertet werden. Oder wenn Zeichen in UTF-8 Strings gesucht werden. Aus diesem Grund benutzt Qt's QString Klasse wohl UTF-16.

Beispiele:

    std::string UTF8laecheln1 = "Lächeln!";
    std::string UTF8laecheln2 = u8"L\u00e4cheln!";
    std::cout << "locale    : " << UTF8laecheln1 << " size: " << UTF8laecheln1.size() << "\n";
    std::cout << "UTF-8     : " << UTF8laecheln2 << " size: " << UTF8laecheln2.size() << "\n";
    std::cout << u8"\u2581" << u8"\u2582" << u8"\u2583" << u8"\u2584" << u8"\u2585" << u8"\u2586" << u8"\u2587" << u8"\u2588";
locale    : Lächeln! size: 9
UTF-8     : Lächeln! size: 9
????????

Scheint als wäre mein Code Editor schon auf UTF8 eingestellt, der Browser wohl nicht. Die Konvertierung nach UTF-16 ist leider schon wieder so ein Brainfuck, dass ich obwohl mit Beispiel nicht geschafft habe.

Im Unicodeblock 00 findet ihr die Umlaute.
ä 00E4
Ä 00C4
ö 00F6
Ö 00D6
ü 00FC
Ü 00DC
Unicodeblock_Lateinisch-1, Ergänzung

02.01.2018

CAD/CAM heightmap Part 2

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

dolphin

Dieses mal versuche ich eine rotierte Heightmap zu erzeugen. Dabei wird das Modell um die Y Achse rotiert und zwar um den Mittelpunkt von Modell.

    // origin y is z
    Point3D rotateAroundY(const double angle, const Point2D& origin) const {
        const auto c = std::cos(deg2rad(angle));
        const auto s = std::sin(deg2rad(angle));
        const auto xnew = c*(x()-origin.x()) - s*(z()-origin.y()) + origin.x();
        const auto znew = s*(x()-origin.x()) + c*(z()-origin.y()) + origin.y();
        return Point3D{xnew, y(), znew};

    }

Das Endergebnis sieht auf den ersten Blick etwas komisch aus, aber ich denke, es ist richtig. Einmal hat man die Ansicht von Unten. Dann seitlich und von Oben, mit der Rückenflosse.

heightmapRot

27.12.2017

34C3

Filed under: Allgemein — Thomas @ 23:12

Demystifying Network Cards
Network cards are often seen as black boxes: you put data in a socket on one side and packets come out at the other end - or the other way around.
Let's have a deeper look at how a network card actually works at the lower levels by writing a simple user space driver from scratch for a 10 Gbit/s NIC.

Interessant.
https://fahrplan.events.ccc.de/congress/2017/Fahrplan/events/9159.html
https://media.ccc.de/v/34c3-9159-demystifying_network_cards

Digitale Bildung in der Schule
„5.-Klässlerinnen, die über die Millisekunden für einen delay()-Aufruf diskutieren!
Gibt es nicht? Doch, gibt es!“ Ein Modellprojekt mit sieben Schulen in Aachen hat diese Frage untersucht – wir haben die Schülerinnen und Schüler begleitet und würden gerne darüber berichten, denn wir wissen jetzt: Programmieren macht ihnen Spaß!

Interessant. Schüler und C++ habe ich noch nie gesehn. Aber wenn selbst Kinder in der 5. Klasse damit kein Problem habe, müssen alle anderen Menschen die mit C++ nicht zurecht kommen, schon verkalte Gehirne haben ;)

Ich persönlich hätte die Frage gestellt, ob ein Kind mal delay(5), delay(1) oder gar delay(0.5) versucht hat. Ob da nur Ganzzahlen funktionieren und ob die delay Funktion selbst einen gewissen overhead hat.

https://fahrplan.events.ccc.de/congress/2017/Fahrplan/events/8865.html
https://media.ccc.de/v/34c3-8865-digitale_bildung_in_der_schule#t=2025

« Newer PostsOlder Posts »

Powered by WordPress