Dienstag, 8. Mai 2018

Kleine Hilfstools, trainiere deine C++ Kenntnisse

Kleine Hilfstools, trainiere deine C++ Kenntnisse

Ich vergleiche C++- Entwickler gerne mit Leistungssportlern, die jeden Tag trainieren müssen. Nur so können sie ihre Leistungen kontinuierlich steigern. Das ist bei Programmieren nicht anders, nur sind es nicht die Muskeln und Reflexe, es sind neue Techniken. So weit her ist dieser Vergleich also nicht gezogen. Warum, zum einen weil programmieren nicht so einfach ist, und speziell C++ erweiterte Anforderungen an den Kenntnisstand stellt, und es nicht jeder kann, zum anderen aber auch, weil wir in Projekten nur Spracheigenschaften nutzen werden, die wir sicher beherrschen.

Nur, wie können wir sie trainieren, wenn nicht in unseren aktuellen Projekten? Uns begegnen in der täglichen Arbeit immer wieder Aufgaben, für die wir kleine Hilfstools brauchen. Aber anstatt sie schnell selber zu schreiben, fangen wir an im Internet zu suchen und ärgern uns, wenn es nicht 100% zu dem passt, was wir eigentlich suchen. Und genau hier ist die Lösung, wir schreiben uns kleine Hilfstools und lösen damit nicht nur unsere konkreten Probleme rund um die Projekte, wir trainieren mit den aktuellsten Compilern einfach neue Techniken.


Ein solches Problem war für mich das Formatieren der Quelltexte für diesen Blog, was mich  in den ersten Blogpost einiges an Zeit und Nerven geraubt hat. Man muss hier in den HTML- Modus wechseln, im Quellcode die Zeichen '<' und '>' gegen die HTML- Codes ersetzen, und wenn man wirklich sicher sein möchte auch die '&' Zeichen. Außerdem schreibe ich meine Quelltexte meistens mit einer Einrückung von 3 Zeichen, aber hier ist der Tabulator- Wert größer. Und manchmal rutscht eben doch der Tabulator in den Quellcode und so verschiebt sich die Anzeige hier.

Deshalb habe ich mich entschlossen, ein kleines Tool zu schreiben, ich habe mir hierfür eine einfache VCL- Anwendung erstellt. Die gesamte Entwicklungszeit betrug weniger als 5:00 Minuten. Ein Bruchstück der Zeit, die ich vorher gebraucht habe, um die Quelltexte manuell zu korrigieren. Und ich würde sicher auch mehr Zeit damit verbringen, etwas passendes im Internet zu suchen. Wir sind die Architekten der Systeme, wir können mit C++ jedes Problem schnell und effizient lösen. Und nebenher habe ich den Umgang mit der Standardbibliothek wieder einmal trainiert. 

Unsere Anwendung hat folgendes Hauptformular. Sicher könnte ich es jetzt erweitern, zum Beispiel einen Schalter zum Laden einer Quelldatei, oder Schalter zum Einfügen und Kopieren in die Zwischenablage, und nicht zu letzt einen Schalter zum Schließen der Anwendung. Vielleicht werde ich das später nachholen, aber es hat mit der Kernfunktionalität nichts zu tun. Und nur um die wollte ich mich im ersten Schritt kümmern.




Für das Memofeld habe ich die Schriftart auf "Courier New" gesetzt, außerdem die Eigenschaft "ScrollBars" auf "ssBoth". Nach dem das Programm gestartet ist, kann ich den gewünschten Quellcode in das Memofeld kopieren und den Schalter zum Transformieren drücken. 

Dann habe ich in der IDE das Ereignis "OnCreate" gesetzt und die Methode implementiert. Als letztes habe ich mit deinem Doppelklick auf den Schalter "btnTransform" die Ereignismethode erzeigt und für den Schalter implementiert. Das folgende Listing zeigt den Quelltext, den ich natürlich schon so bearbeitet habe, bevor ich ihn hier eingefügt habe.

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "MainForm.h"

#include <string>
#include <algorithm>
#include <functional>
using namespace std;
using namespace placeholders;
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmMain *frmMain;
//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { }

//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormCreate(TObject *Sender) {
   Caption               = L"Hilfsprogramm für Blog";
   Panel1->Caption       = L"";
   btnTransform->Caption = L"Transformiere";
   Memo1->Lines->Clear();
   }

//---------------------------------------------------------------------------
void mytransform(char para, string& strTarget) {
   switch(para) {
     case '&' : strTarget += "&amp;"; break;
     case '<' : strTarget += "&lt;";  break;
     case '>' : strTarget += "&gt;";  break;
     case '\t': strTarget += "   ";   break;
     default:
       {
       char szBuffer[2] = { para, 0 };
       strTarget += szBuffer;
       }
     }
   return;
   }


void __fastcall TfrmMain::btnTransformClick(TObject *Sender) {
   string strSource = AnsiString(Memo1->Text).c_str();
   string strTarget = "";
   strTarget += "<pre class=\"brush: C++\">\r\n";
   for_each(strSource.begin(), strSource.end(), bind(mytransform, _1, ref(strTarget)));
   strTarget += "\r\n</pre>\r\n";
   Memo1->Text = strTarget.c_str();
}
//---------------------------------------------------------------------------


In der Methode "FormCreate" setze ich die Überschrift und Beschriftungen. Die meisten von Ihnen machen das eventuell im Objektinspektor in der IDE. Dabei gibt es zwei Nachteile. Hier im Bild oberhalb des Listings können sie wesentliche Teile des Formulars noch erkennen, und es in der Dokumentation verwenden. Ich habe das Bild mit SnagIt erstellt, und könnte auch noch zusätzliche Kommentare oder Markierungen ergänzen. Das Bild kann dann auch mit Doxygen oder ähnlichen sehr guten Tools im C++- Umfeld eingebunden werden, oder in einer Wiki hochgeladen werden. Zum anderen können sie ohne delphilastige Hilfstools nicht Internationalisieren, und es steckt zuviel Logik in den Formularen selber. Sie haben in den ersten Beispielen gesehen, wie leicht es ist, die VCL oder FMX aus C++ anzusprechen, auch aus einem Quelltext heraus. Das kann natürlich auch auf andere Frameworks wie Qt oder MFC erweitert werden. C++ ist der Industriestandard, also sollten sie möglichst viel im Quelltext machen, nicht in der IDE. Das weite Teile des Programms nicht nur unabhängig vom Framework sondern auch von speziellen Formularen sind. Dafür dürfen sie aber nicht dfm- oder fmx- Dateien nutzen, um ihre konkreten Beschriftungen festzulegen.

Die eigentliche Konvertierung der einzelnen Zeichen wird in der globalen Funktion void mytransform(char para, string& strTarget) durchgeführt. Diese bekommt das zu konvertierende Zeichen als ersten und die Referenz auf die Stringvariable, die den konvertierten  Text speichert, als zweiten Parameter. Da ein Zeichen immer in eine Sequenz umgewandelt werden kann, ist eine Rückgabe als char Wert so nicht möglich. Deshalb ist es sinnvoll, den Ergebnisstring als Parameter zu übergeben. Auch wenn wir damit erstmal die Möglichkeit verlieren, diese Methode als Funktor für die Standardbibliothek zu nutzen. Innerhalb der Mehrfachauswahl können jetzt die Zeichen einzeln behandelt werden. Hier können auch beliebig Zeichen ergänzt werden, sollte es notwendig sein.

In der Ereignismethode zu dem Schalter "btnTransform" wird eine C++- Stringvariable "strSource" definiert, in der ich den kompletten Inhalt des Memo- Feldes kopiere. Damit kann ich mich in der weiteren Bearbeitung auf C++ stützen. Bei dem Quellcode dürfte es sich meistens um nationalen Code handeln, deshalb nutze ich die interne Konvertierung von Delphi und wrappe das Eingabefeld in eine temporäre Variable vom Typ AnsiString und nutze die Methode c_str() um den C++- String zu initialisieren. Dazu definiere ich eine zweite, leere Stringvariable "strTarget", diese soll später den geänderten Inhalt übernehmen. 

Dann nutze ich den C++- Algorithmus for_each() um für jedes Zeichen der ersten Zeichenkette den Funktor aufzurufen. Diese darf entsprechend der Syntax nur einen unären Funktor haben, in unserem Fall eine Methode mit einem char- Zeichen als Paramter. Um die Methode passend zu machen nutze ich die C++11- Methode bind() und binde den zweiten Parameter als Referenz an die 2. Stringvariable "strTarget". Dadurch entsteht ein unärer Funktor, nur das char- Zeichen bleibt als Parameter, der aber genau zum Inhalt einer Variable vom Typ std::string passt. Vor und nach dem Konvertieren schreibe ich noch die notwendigen <pre ...> und </pre> Tags in die Zeichenvariable.

Nach erfolgreicher Konvertierung wird der Inhalt der Variable "strTarget" in das Memo- Feld geschrieben, und kann von dort wieder herauskopiert werden.

Dieses kleine Beispiel zeigt einen kleinen Helfe für die tägliche Programmierung. Und davon gibt es viele. Bis hin zu einem Refaktoring, Suchen von Dateien auf ihrer Festplatte, durchsuchen von Projektdateien, ... mir fallen viele Beispiele aus dem täglichen Umfeld ein. Nutzen Sie diese um ihre C++- Fähigkeiten zu trainieren, neueste Compiler einzusetzen und neue Techniken auszuprobieren. Dabei können Sie, wie dieses Beispiel gezeigt hat, sogar Zeit sparen. 

Um noch einmal zu dem Beispiel mit dem Leistungssportler und dem Training. Auch ein Sportler erreicht dieses Ziel meistens nur in einem Team, und mit Hilfe von Trainern und Betreuern. Auch hier ist der Unterschied nicht so groß. Der Austausch untereinander ist wichtig. Dafür können Roadshows and Events gut sein, eine örtliche Gruppe oder Kollegen. So kann eine in anderen Bereichen bestrafte Raucherpause gut für den Austausch sein. Aber auf der anderen Seite gibt es auch im Softwarebereich Berater und Trainer. Natürlich auch entsprechende Lektüre.
;