Markiert mit "PROGRAMMIEREN"


Wie schwer kann das schon sein?

Weil mir die gebräuchlich gewordene penetrante Kommerzialisierung selbst der banalsten Smartphone-Spielchen zuwider ist und ich nicht mag, dass der Nachwuchs beim Handydaddeln ständig Werbung und Tracking ausgesetzt ist, hatte ich mir überlegt, einfach mal ein Programm für Android-Smartphones selbst zu schreiben. Ein kleines Spielchen für zwischendurch. Statt immer nur für den PC oder für Mikrocontroller zu entwickeln. Kann ja nicht sooo schwer sein - Apps gibt es ja inzwischen wie Sand am Meer und völlig fremd ist mir die Handy-Welt ja auch nicht. Also mal schnell schlau machen, wie das heute so läuft - und erst mal entsetzt Resignieren: da hat sich technisch ja überhaupt nix zum Besseren entwickelt! Das ist auf modernen Smartphones im Kern immer noch der gleiche Java-Ranz von vor zwanzig Jahren. Nur jetzt halt mit hippen Namen und weiteren Bloat-Schichten drumherum. Gewachsene Strukturen wie ein verwahrloster Unkrautgarten. Bah. Da hätte ich eigentlich mehr erwartet. Da wundert es nun auch nicht, was das alles für ein Security-Totaldesaster ist. Nun ja, zum Glück will ich ja keine Banking-App machen, sondern bloß ein Spiel. Also rein in den Sumpf. Zuerst habe ich die harte Tour gewählt, dicke Bücher über Android und App-Entwicklung studiert, Android-Studio installiert und mich eine ganze Weile soweit damit beschäftigt, dass ich nun schöne Apps schreiben kann, die irgendwelche Daten entgegennehmen, verarbeiten, abspeichern, wieder laden, und so weiter und so fort. Für die persönliche Entwicklung als Programmierer ist das ja ganz nett und früher oder später werde ich sicher auch wieder davon Gebrauch machen, aber um ein Spiel zu erstellen war mir das alles zu zäh und zu aufgeblasen. Auf der Suche nach etwas Besserem fiel mir Clickteam Fusion ein, mit dessen Urgroßvater-Version "Klick&Play" von 1994 ich schon anno dazumal kleine Windows-Spiele erstellt hatte. Hach, die gute alte Zeit! 😍 Inzwischen ist das natürlich sehr viel weiter entwickelt und kann auch nach Android exportieren. Prima. Nehm' ich. Mit so einem Spiele-Baukasten geht das Ganze freilich viel leichter von der Hand und man kann sich auf das Wesentliche konzentrieren. Trotzdem kann man auch mit probaten Mitteln reichlich Zeit damit verbringen so ein "kleines Spielchen" zu erstellen. Zumal ich nur hin und wieder mal abends oder am Wochenende daran gearbeitet habe - man hat ja schließlich noch reichlich anderes zu tun. So hat sich das letztlich über die ganze Corona-Zeit hingezogen, bis ich nun heute sagen kann: ist fertig genug.
Eierschleuder mobile game app Eierschleuder mobile game app
Das Resultat ist ein Android-Game namens Eierschleuder, bei dem man eine Schleuder spannt und Eier auf kleine gelbe Männchen katapultiert. Weil's Spaß gemacht hat und mir bei solchen Projekten dann schnell mal der eigene Anspruch durchgeht, ist es letzten Endes durchaus vorzeigbar geworden mit netter Grafik, dutzenden Levels in verschiedenen Welten, unterschiedlichen Wurf-Geschossen und haufenweise Special Effects 😂 Und natürlich alles als kostenlose App, ohne Werbung, Tracking oder sonstigen Schrott. In den Google-Playstore habe ich es allerdings nicht hochgeladen, weil ich dazu erst für 25,- € eine Entwicklerlizenz erwerben müsste und das ist es mir im Augenblick schlicht nicht wert. Wer also mag, kann Eierschleuder hier direkt auf's Android-Smartphone herunterladen (ca. 32 MB) und ausprobieren oder sich auf der Projektseite erst mal noch ein paar Screenshots ansehen. Viel Spaß!

Veraltet und vergessen

Nehmt euch doch auch ein Glas Wein, heute philosophiert Herr Cypax mal ein bisschen vorm Kaminfeuer...
Wireless Toolkit
Vom Aussterben bedroht: alte Programmiertools
Wir haben einen Pkw, der ist 11 Jahre alt. Eine Waschmaschine, die ist 15 Jahre alt. Meine Nähmaschine ist über 30. Und im Keller ist noch ein Bakelittelefon aus den 1950ern angeschlossen. All diese technischen Dinge sind immer noch in Betrieb, sehen nicht groß anders aus als ein Neugerät (ok, ja, das Telefon schon etwas) und funktionieren auch anno 2021 nicht grundsätzlich schlechter, als es ein Neugerät würde. Deutlich anders sieht das schon bei den Computern aus. Und extrem auffällig ist der Technikwandel bei Handys. Das ist mir letztens wieder so richtig gewahr geworden, als ich für den neuen Bombenwecker ein Nokia von 2007 wieder in Betrieb genommen habe, um dafür eine Anwendung zu programmieren. 2007 steckten Android und iOS noch in den Babywindeln und es war noch nix groß mit Smartphones, Apps und derlei. Die Handys hatten mickrige Grafikdisplays, wenig Speicher und "Apps" waren im Wesentlichen kleine Java-Programme, die in einer sehr rudimentären Laufzeitumgebung liefen. Und normalerweise findet man im Netz ja zu jedem beliebigen Thema Informationen in unbewältigbarem Überfluss - um aber herauszufinden, wie und womit ich genau dieses Handy programmiere, wo man noch eine Entwicklungsumgebung herbekommt, wo eine Laufzeitumgebung und so weiter, musste ich erstmal richtig ausgiebig graben. Und ja, gefunden habe ich dann letzten Endes schon noch irgendwie alles. Aber es stellte sich dabei auch dieser Eindruck ein, sich auf nahezu völlig ausgestorbenem Gebiet zu bewegen. Fast wie ein Archäologe. Klar, von all den Bibliotheken, Konzepten und Quelltexten aus dieser Zeit ist natürlich auch einiges in Neuentwicklungen eingeflossen und lebt damit bis heute in gewisser Form weiter. Auch heutige Android-Apps beispielsweise basieren letztlich alle auf Java. Und manche IT-Themen überleben sogar dauerhaft in kleinen ökologischen Nischen. So wie manche Bankenanwendung noch in Fortran geschrieben ist oder es immer noch Geldautomaten mit Windows XP gibt. Und folglich gibt es dazu auch noch entsprechende Nischen-Communities, wo auch heute noch zumindest geringe Aktivität besteht. Bei der Handy-Anwendung á la 2007 hingegen stellte sich so ein richtiges Friedhofsgefühl ein. Die Art, wie noch vor wenigen Jahren Handyprogramme entwickelt wurden, all die Forenbeiträge, Diskussionen, Tools und Tutorials sind mausetot und kommen auch nie wieder. Da kommt man schon mal ins Grübeln, was das für eine bizarr kurzlebige Branche ist.

Warum man fremdem Code nicht trauen darf

main():
  1. int a = 0;
  2. printf("Der Wert von Variable 'a' ist %d.\n", a);
  3. BoeseFunktion(4711);
  4. a = 1;
  5. printf("Der Wert von Variable 'a' ist jetzt %d.\n", a);
Was meint ihr, gelingt es mir als Programmierer von BoeseFunktion() dass die Ausgabe in der zweiten Zeile Der Wert von Variable 'a' ist jetzt 0. lautet? Selbst wenn 'a' nur lokal der Main-Funktion bekannt ist? Ich sage ja:
BoeseFunktion():
  1. void BoeseFunktion(int tmp) {
  2.   int* pointer;
  3.   pointer = &tmp - 1;
  4.   *pointer = *pointer + 8;
  5. }
Das funktioniert eigentlich ganz einfach. Mit pointer = &tmp - 1; hole mich mir einen Zeiger auf den ersten Funktionsparameter und reduziere die Adresse um 1. Auf x86-Systemen bin ich nun an der Rücksprungadresse von BoeseFunktion() auf dem Stack. So sieht das im Speicher aus: Stack Und mit *pointer = *pointer + 8; erhöhe ich anschließend die Rücksprungadresse um 8 Byte, was gerade soviel ist um das a = 1; in main() zu überspringen. Solches Gehacke mit Pointer-Arithmetik ist natürlich sowieso pfui. Aber was uns dieses Beispiel eigentlich lehren soll ist, dass man fremden Funktionen nicht trauen soll. Es könnte schließlich sein, dass ich sie geschrieben habe... 😉

Schadensabwehr

Aber wie bekommt man nun das Problem in den Griff, dass Fremdcode, den man in so gut wie jedem Projekt findet, unbeabsichtigt oder beabsichtigt Unfug anstellt und Daten oder die Programmausführung beeinträchtigt? Da gibt es mehrere Möglichkeiten. Unter anderem:
  • Logische Programmablaufkontrolle Man stelle sich vor, jede eigene Funktion meldet sich bei ihrem Aufruf an einer zentralen Instanz mit einem eindeutigen Key. Die zentrale Instanz überprüft dann anhand einer Tabelle ob es in Ordnung ist, dass Funktion3 nun auf Funktion2 folgt oder nicht. Bringt fremder Code - zum Beispiel eine defekte DLL - den Ablauf durcheinander so wird dies schnell aufgedeckt. Voraussetzung für ein solches System ist natürlich eine deterministische Reihenfolge der Funktionsaufrufe.
  • Datenredundanz Zuerst einmal sollten alle Daten, die irgendwie wichtig sind, redundant vorliegen; am besten redundant-invers. Das heißt, es gibt nicht eine Variable 'a', sondern z.B. 'unsigned char u8VentilSollwert' und 'unsigned char u8VentilSollwertInvers'. Das hat nicht nur einen eingängigeren Namen als 'a' sondern informiert uns auch gleich noch über das Präfix u8, dass es eine unsigned 8-Bit-Variable ist mit Wertebereich von 0..255. In der einen steht dann z.B. 1 (binär 0000 0001) und in der anderen das Gegenteil, also 254 (1111 1110). Vor Verwendung wird jedes Mal geprüft, ob die beiden Variablen noch Bit-invers sind und falls nicht mit Fehlermeldung abgebrochen.
  • Datensicherung Wenn die Daten umfangreicher sind, gibt es die Möglichkeit sie mit einer Checksumme zu sichern. Wird die Checksumme oder die Daten beschädigt, so fällt dies auf, sobald man die Checksumme nachrechnet und mit dem Prüfwert vergleicht.

Qt: bloß keine Waisenkinder instantiieren

Wenn man in Qt eine Klasse implementiert, sollte man tunlichst darauf achten, dass alle instantiierten Attribute this als parent haben. Andernfalls kann es nämlich sein, dass irgendwann der Garbage-Kollektor auf das offenbar elternlose Kind-Objekt stößt und es im Sinne von "ist das Kunst oder kann das weg" in die Tonne wirft. Und dann hat man den Salat. Ist mir hiermit passiert:
  1. class Measurement : public Object
  2. {
  3. Q_OBJECT
  4. public:
  5.   Measurement(QObject* parent = 0);
  6.   ~Measurement();
  7. };
  8. class XYPlot : public QQuickPaintedItem
  9. {
  10. Q_OBJECT
  11. public:
  12.   XYPlot(QQuickPaintedItem* parent = 0);
  13. private:
  14.   Measurement m_Measurement;
  15. };
Das Elternelement hier ist XYPlot, welches ein Attribut der Klasse Measurement besitzt. Bei mir gab es unreproduzierbare SIGSEGV segmentation faults, was immer auf irgendein Problem mit ungültigem Pointer hinweist. Beim Debuggen habe ich dann festgestellt, dass kurz zuvor der Dekonstruktor von Measurement aufgerufen wurde - obwohl das zugehörige XYPlot-Objekt noch existierte. Woran lag es? ⇒ Ich hatte vergessen, dem Kind-Objekt zu sagen wer sein parent ist. Im Konstruktor der Elternklasse XYPlot also m_Measurement mit this instantiieren:
  1. XYPlot::XYPlot( QQuickPaintedItem* parent) : QQuickPaintedItem(parent), m_Measurement(this)
  2. {
  3. }
Also besser gut aufpassen bei so was, sonst kann das Debugging richtig ätzend werden.