C++Guns – RoboBlog blogging the bot

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

23.11.2017

C++ Guns: RE: Abstraction design and implementation: `repeat` Teil 2

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

In Teil 1 ging es um die allgemeinen Probleme einer simplen for() Schleife.
In Teil 2 werde ich Vittorio Romeo Ansatz verfolgen, eine repeat() Funktion zu erstellen, die eine Funktion f genau N mal aufruft.

Syntaktisch und semantisch würde ich diesen Code okay finden:

#include <type_traits>
#include <iostream>

void f() {
  cout << "f()\n";
}

template<typename Func>
void repeat(int n, Func&& f) {
  for(int i=0; i < n; ++i) {
    f();
  }
}

int main() {
  repeat(10, f);
}

Die Funktion f wird einfach 10 mal aufgerufen. Das war einfach.

Sobald ich mehr tun will, wirds kompliziert.

Schritt für Schritt. Wir werden sehen wann es nicht mehr klappt.
Möchte ich der Funktion f einen Parameter mitgeben, der nicht von der aktuellen Iterationen abhängt, lässt sich das leicht mit einem Lambda realisieren, welches ein Interface darstellt. Dieses Interface bildet meine Funktion f(int) auf eine Funktion ohne Argument ab. Also genau so eine Funktion, welche repeat() erwartet.

void f(int parameter) {
  cout << "f() parameter " << parameter << "\n";
}

int main() {
  int parameter = 42;
  repeat(10, [parameter]() {
    f(parameter);
  });
}

Die Funktion repeat() ändert sich nicht. Das ist schon einmal ein gutes Zeichen.
Jetzt wird es kritisch. Ich möchte neben dem Parameter noch die Laufvariablen der repeat Funktion mit nach f() übergeben. Dafür benutze ich wieder ein Lambda welches das Interface bereit stellt und auch if constexpr und std::is_invocable_v. Also hight end C++17 Shit :D Mal sehn obs kappt.

void f(int i, int parameter) {
  cout << "f() i: " << i << " parameter: " << parameter << "\n";
}

template<typename Func>
void repeat(int n, Func&& f) {
  for(int i=0; i < n; ++i) {
    
    if constexpr(std::is_invocable_v<Func&&, int>) {
      f(i);
    } else {
      f();
    }
  }
}

int main() {
  int parameter = 42;
  repeat(10, [parameter](int i) {
    f(parameter, i);
  });
}

Der Code compiliert mit einem C++17 Compiler (gcc 7) und liefert folgende Ausgabe:


f() i: 42 parameter: 0
f() i: 42 parameter: 1
f() i: 42 parameter: 2
f() i: 42 parameter: 3
f() i: 42 parameter: 4
f() i: 42 parameter: 5
f() i: 42 parameter: 6
f() i: 42 parameter: 7
f() i: 42 parameter: 8
f() i: 42 parameter: 9

Das ist nicht das, was ich wollte. Ich habe absichtlich die Funktions Argumente parameter und i vertauscht und der Compiler hats nicht gemerkt! Wie sollte er auch. Die Variable i ist nur ein int Type und hat keinerlei Semantik einer Laufvariablen.

Das hat mich am Artikel "abstraction design and implementation: `repeat`" auch so stark gestört. Damit es ist super einfach lazy Bugs zu erzeugen und am Ende hat man nichts gewonnen.

Im dritten Teil will ich versuchen das C++ Typesystem zu nutzen, um die Semantik einer Laufvariablen auszudrücken und im letzten Beispiel einen Compilerfehler zu erzwingen.

C++ Guns: RE: Abstraction design and implementation: `repeat` Teil 1

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

Hallo,
ich möchte hier ein paar Gedanken äußern zu dem tollen Artikel "abstraction design and implementation: `repeat`" von Vittorio Romeo [1].

Sein Ziel ist es eine simple for() Schleife, wie wir sie schon alle hingeschrieben haben, zu nehmen und zu verbessern. Das Problem bei einer voll ausgeschriebenen for() Schleife ist, dass sie zeigt WAS alles gemacht wird: Eine Schleifenvariable declakiert, initialisiert, incrementiert. Aber was willen wir eigentlich damit aussagen? Na, rufe die Funktion f() einfach 10 mal auf. Ahsoo!

for(int i=0; i < 10; ++i) {
    foo();
}

Das Problem an allen Codestücken die bis heute geschrieben worden sind, ist dass ihnen die Semantik fehlt. Der Code sagt immer nur auf, WIE etwas gerade verarbeitet wird, aber nicht WAS eigentlich getan werden soll. Wenn man Glück hat, steht das WAS in irgendeiner veralteten Dokumentation oder Kommentarzeilen die sich irgendwo befinden.

Aber in diesem Blogpost will ich nicht lehren zu Dokumentieren, sondern die Leute sensibel dafür machen, dass sie das, WAS sie wollen, auch genauso in Code ausdrücken können.

Kommen wir zurück zur for() Schleife.
In dem Code Beispiel oben steckt ziemlich viel low-level Technik. Es wird erst einmal eine Schleifenvariable i vom Typ int deklariert und mit 0 initialisiert. Und Stopp. Hier sind schon zwei lazy Bugs passiert.
Wer sagt denn, dass der Typ int sein soll? Wenn man über std::vector o.ä. Iteriert, sollte der Typ size_t sein, da std::vector::size() auch size_t zurück liefert. Und auch operator[] und at() jeweils ein size_t als Argument erwarten. Wenn ich eine Schleife aber nur 10mal laufen lassen will, dann ist der Typ int wiederum meist in Ordnung.

Die Initialisierung mit 0 ist auch nicht zwangsläufig richtig. Zwar fängt der Index in C/C++ bei 0 an, aber wenn man eine Schleife nur 10mal laufen lassen will, ist es doch egal ob man bei 0 anfängt und bis 9 zählt, oder ob man bei 1 startet und bei 10 endet.

Das bringt uns gleich zum nächsten lazy Bug. Der Ausdruck i < 10 ist so nicht intuitiv. Der Mensch beginnt nun mal bei 1 ab zu zählen und endet bei 10. Daher schreiben viele, noch nicht gefolterte C++ Programmierer, zurecht i <= 10.

Der nächste lazy Bug kommt noch in der selben Code Zeile. Ist es nun ++i oder i++? In diese Beispiel wäre es egal, da int ein POD ist. Ansonsten gilt, dass ++i vorzuziehen ist, da keine temporäre Variable erstellt (und evtl. wieder weg optimiert) werden muss.

Der letzte lazy Bug sehe ich leider auch sehr oft. Die Variable i wird nicht innerhalb der for() Schleife deklariert, sondern nur innerhalb der Funktion. Also altes C/Fortran. So ist es möglich, absichtlich oder aus versehen, die Variable i mehrmals zu nutzen. In der Regel ist dies nicht gewollt, und wenn doch, wird es nicht dokumentiert.

Es kann nicht sein, dass auf so viele Sachen geachtet werden muss, wenn eine simple Schleife gefordert ist. Vittorio Romeo zeigt zurecht wie es einfacher geht. Leider macht er so die Sache nicht besser, nur anders schlimm.

Teil 2

[1] https://vittorioromeo.info/index/blog/abstraction_design_implementation_repeat.html

02.11.2017

C++ Guns - Addressen von Referenzen

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

Haben Referenzen in C++ eigene Adressen, so wie normale Variablen und Pointer, oder haben sie nur die Adresse der referenzierten Variablen?
Wenn Referenzen eigene Adressen besitzen, existieren sie wie normale Variablen im RAM. Ich meine mich aber erinnert zu haben, dass eine Referenz nur ein Alias für eine andere Variable ist. Einfach nur syntactic sugar für den Programmierer. Das wäre natürlich optimal, da unnötige Kopieraktionen entfallen.
Ein kleines Experiment bringt Klarheit:

void func(int& c) {
  cout << "In func(): Adress of c reference to a: " << addressof(c) << "\n";
  c = 1;
}

int main() {
  int a = 0;
  cout << "Value of a: " << a << "\n";
  cout << "Adress of a: " << addressof(a) << "\n";
  int &b = a;
  cout << "Adress of b reference to a: " << addressof(b) << "\n";
  func(a);
  cout << "Value of a: " << a << "\n";
}
Value of a: 0                                                                                                                                                                       
Adress of a: 0x7ffd58cf22b4                                                                                                                                                         
Adress of b reference to a: 0x7ffd58cf22b4                                                                                                                                          
In func(): Adress of c reference to a: 0x7ffd58cf22b4                                                                                                                               
Value of a: 1

Es gibt nur eine einzige Variable im Programm, mit der Adresse 0x7ffd58cf22b4. Es ist egal, ob über eine Referenz darauf zugegriffen wird, oder sogar aus einer Funktion heraus, über eine Referenz, die Variable a manipuliert wird. Es werden nur 4Byte Speicher im RAM belegt. Wunderbar.

Mit Pointer sollte dieser Effekt nicht zu erreichen sein. Da Pointer ja echte Variablen mit eigenen Adressen.

void func(int* c) {
  cout << "In func(): Adress of c pointer to a: " << addressof(c) << "\n";
  *c = 1;
}

int main() {
  int a = 0;
  cout << "Value of a: " << a << "\n";
  cout << "Adress of a: " << addressof(a) << "\n";
  int* b = addressof(a);
  cout << "Adress of b pointer to a: " << addressof(b) << "\n";
  func(addressof(a));
  cout << "Value of a: " << a << "\n";
}
Value of a: 0
Adress of a: 0x7ffc955db71c
Adress of b pointer to a: 0x7ffc955db710
In func(): Adress of c pointer to a: 0x7ffc955db6e8
Value of a: 1

Ganz klar zu sehen, jeder Pointer hat eine andere Adresse als Variable a. Es werden also 3*4Byte = 12Byte Speicher im RAM belegt. Plus zusätzliche Kopieraktionen der Adresse von a hin zur Pointervariablen.

Interessanterweise sind Referenzen im C Sprachstandard nicht vorgesehen. Somit ist es nicht möglich, dieses simple Beispiel auf Laufzeit hin optimiert in C zu programmieren. Nur in C++.

27.10.2017

C++ Guns - multi derived CRTP - more pain

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

And because we love pain, here come the CRTP diamond derive.
Have fun with these. (no, don't)

  #include <iostream>

  using namespace std;

  template<typename Child, typename Child2>
  struct UltraBase : public Child, public Child2 {
    void func() {
      cout << "UltraBase\n";
      static_cast<Child*>(this)->func();
    }
    
    void func2() {
      cout << "UltraBase\n";
      static_cast<Child2*>(this)->func2();
    }
  };
  
  template<typename Child>
  struct Base : public Child {
    void func() {
      cout << "Base\n";
      static_cast<Child*>(this)->func();
    }
  };

  template<typename Child>
  struct Base2 : public Child {
    void func2() {
      cout << "Base2\n";
      static_cast<Child*>(this)->func2();
    }
  };

  struct Child {
    void func() {
      cout << "Child\n";
    }
    
    void func2() {
      cout << "Child2\n";
    }
  };


  int main() {
    UltraBase<Base<Child>, Base2<Child>> ultrabase;
    ultrabase.func();
    ultrabase.func2();
  }
UltraBase
Base
Child
UltraBase
Base2
Child2

C++ Guns - multi derived CRTP

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

As we all know, CRTP is bad. The reasons against you can read on my unpublish Artikel "Why CRTP is a bad choise".

Nevermind. Here come a CRTP with a linear derived chain.

#include <iostream>

using namespace std;

template<typename Child>
struct Base : public Child {
  void func() {
    cout << "Base\n";
    static_cast<Child*>(this)->func();
  }
};

template<typename Child>
struct Base2 : public Child {
  void func() {
    cout << "Base2\n";
    static_cast<Child*>(this)->func();
  }
};

struct Child {
   void func() {
     cout << "Child\n";
   }
};


int main() {
  Base<Base2<Child>> base;
  base.func();
}
Base
Base2
Child

24.09.2017

C++ Guns - Settings class

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

Für das nächste große Projekt will ich Einstellungen aus einer Datei lesen und in Performance kritischen Codeabschnitt nutzen.
Die Variablen sollen read-only, also const, sein. So wird ein versehentlichen verändern der Einstellungen verhindert und der Compileroptimierer freut sich auch. Auf getter-Funktionen möchte ich wegen unnötigen Code verzichten.
Wenige Variablen werden besonders häufig benutzt, daher soll keine zusätzliche Tipparbeit entstehen, um erst auf das Einstellungs-Objekt zuzugreifen, und dann auf die eigentliche Variable. Statt

if(settings.L1)

einfach gleich

if(L1)

Also wird es Shortcut Variablen geben.
Um die Lesbarkeit des performance kritischen Codes nicht zu verschlechtern, sollen einige Einstellungs Variablen zur Compilezeit ausgewertet werden, aber erst zur Laufzeit gesetzt. Dies gelingt mittels Code Vervielfältigung. Der Compiler erstellt für jede Variante speziell optimierten Code.

Erste Implementierung:

#ifndef __cpp_if_constexpr
    #error "You need an actual C++17 compiler with feature P0292R2 'constexpr if' for this example"
#endif

#include <iostream>

using namespace std;

struct Settings {
    bool L1 = false;
    bool L2 = false;
};

template<bool L2>
class Algo {
public:
    Algo(const Settings& s)
        : s(s)
    {
    }

    void run() {
        cout << "SWE Settings:\n";
        cout << "L1          " << s.L1 << "\n";
        cout << "shortcut L1 " << L1   << "\n";
        cout << "L2          " << s.L2 << "\n";
        // s.L2 = true; // error. read only

        if constexpr(L2) {
            cout << "L2 template " << L2   << "\n";
        } else {
            cout << "L2 template " << L2   << "\n";
        }
    }

private:
    const Settings s;
    const bool L1 = s.L1;
};


Settings readSettings() {
    Settings s;
    s.L1 = true;
    return s;
}

template<bool L2>
void func(const Settings& s) {
    Algo<L2> swe(s);
    swe.run();
}

int main() {
    cout << boolalpha;

    Settings s = readSettings();

    cout << "Runtime L2 setting: " << s.L2 << "\n";
    if(s.L2) {
        func<true>(s);
    } else {
        func<false>(s);
    }

    s.L2 = !s.L2;
    cout << "\nRuntime L2 setting: " << s.L2 << "\n";
    if(s.L2) {
        func<true>(s);
    } else {
        func<false>(s);
    }
}


12.09.2017

C++ Guns - Missed optimizations

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

Compiler sind nicht perfekt. Gerade das Optimieren von Code ist so komplex, dass es keinen Algorithmus dafür gibt. Ehr werden Heuristiken dafür benutzt. So kommt es schon mal vor, dass bei banal aussehenden Code die Compiler Optimierung versagt hat und es möglich ist, von Hand schnelleren Assembler Code zu schreiben.

In [1] wurde viele solcher fehlerhaften Optimierungen gezeigt. Mit "Fehlerbeschreibung", Assembercode und teils sogar Patches für den Compiler. Leider nur für die ARM Architektur.

Ich möchte nun einige Beispiele aufgreifen und versuchen sie auf X86 64bit Plattformen nachzuahmen. Getestet wird mit g++ 64bit 4.9.2, 7.1.0, 32bit 6.3.0. Compileraufruf:
g++ -save-temps -O3 -g -fverbose-asm -march=native -c 1.cpp -Wa,-adhln=test.s

Compiler passedfailed
gcc 8.0.0 ARM 0 2
g++ 7.1.0 x86-642 0
g++ 6.0.0 x86-321 1
g++ 4.9.2 x86-642 0

Useless initialization of struct passed by value

struct S0 {
  int f0;
  int f1;
  int f2;
  int f3;
};

int f1(struct S0 p) {
    return p.f0;
}

ARM:

The struct is passed in registers, and the function's result is already in r0, which is also the return register. The function could return immediately, but GCC first stores all the struct fields to the stack and reloads the first field.

X86:

11:1.cpp         ****     return p.f0;
71   	         movl	%edi, %eax	# p,
72               ret

Ja, nur eine Assembler Instruktion.

float to char type conversion goes through memory

char fn1(float p1) {
  return (char) p1;
}

ARM:

the result of the conversion in s15 is stored to the stack and then reloaded into r0 instead of just copying it between the registers

X86-64:

 2:2.cpp         ****   return (char) p1;
78         vcvttss2si      %xmm0, %eax     # p1

VCVTTSS2SI: Convert one single-precision floating-point value from xmm1/m32 to one signed doubleword integer in r32 using truncation.

Ja, mit AVX zwischen den Register kopiert.

X86-32:

78               subl    $4, %esp        #,
 2:2.cpp         ****   return (char) p1;
81               flds    8(%esp) # p1
82               fisttps 2(%esp) #
83               movzwl  2(%esp), %eax   #

FISTTP: Store Integer with Truncation

Nein, die Variable wird vom Stack in ein floating point Register geladen und dort convertiert. Danach zurück auf den Stack gespeichert und zum Schluss in das Rückgaberegister geladen.

[1] https://github.com/gergo-/missed-optimizations/blob/master/README.md

« Newer PostsOlder Posts »

Powered by WordPress