Alte Projekte modernisieren – Teil II

Integer- und Fließkommagrößen / Declares

Weil’s so schön und kurz war (wenn Sie nicht wissen was: Teil I nämlich!), gleich noch ein Tipp zur Modernisierung hinterher. Diesen werden Sie hauptsächlich dann beherzigen müssen, wenn ihr Code Declares verwendet – also die direkte Ansprache einer Betriebssystem- oder sonstigen Bibliothek. Das zur Illustration ausgesuchte Beispiel bezieht sich in erster Linie auf macOS, aber keine Angst: Windows- und Linux-Benutzer werden auch profitieren können!

Ich möchte mich ein weiteres Mal bei den freien Projekten von Roger Meier bedienen, und zwar, da es schon in Teil I erwähnt wurde, beim Game of Life-Projekt.

Zur Erinnerung, oder falls der Hype seinerzeit an Ihnen vorbeigegangen ist: Game of Life ist eine weitere Mathematik-Simulation, in der eine Startgeneration von Zellen Entwicklungsschritte erfährt, die ihr weiteres Überleben bestimmen.

Wikipedia erklärt die Berechnungen dazu sehr anschaulich, daher nur ein Kürze: Eine Zelle „lebt“, wenn sie schwarz dargestellt wird. Ihr weiteres Überleben wird durch die Anzahl ihrer lebendigen Nachbarn bestimmt: Bei zu wenigen geht sie an Einsamkeit ein, und sind es zu viele, dann stirbt sie an Überbevölkerung. Durch passende Nachbarzahl kann eine abgestorbene Zelle aber auch wieder zu neuem Leben erwachen. Für die Praxis:

  • Laden Sie das REALife-Projekt von obiger Website und entzippen Sie es. Auch hier: Können Sie es nicht durch Doppelklick öffnen, ziehen Sie die Projektdatei auf das Xojo-Programmsymbol.
  • Starten Sie das Programm.

ReaLife.png

Diesmal werden Sie vermutlich nicht gewarnt. Das Programm ist schon aktualisiert, was auch der Hinweis auf macOS-Retina-Kompatibilität vermuten ließ.

Also alles prima? Auf den ersten Blick ja. Aber vielleicht lässt sich ja noch mehr Geschwindigkeit aus dem Programm kitzeln? Dafür bietet sich eine Kompilierung als 64 Bit-Programm mit aggressiver Optimierung an. Nicht nur, dass die Compiler-Optimierung alleine schon etwas bringt: Insbesondere unter macOS nutzt das Betriebssystem auch einige CPU-Features, die unter 32 Bit nicht verfügbar sind und die für sich alleine eine Performance-Beschleunigung bewirken können.

  • Stellen Sie im Navigator in den Build-Settings die Architektur für Ihr System auf 64 Bit und ebendort in den Shared Settings die Optimierung auf Aggressive.

    Das funktioniert momentan nur, wenn Sie eine Build-Lizenz für Ihr System besitzen. In diesem Jahr soll aber auch das Debuggen unter 64 Bit möglich werden.

  • Kompilieren Sie das Projekt und starten Sie es.

Wie Sie sehen – zumindest auf dem Mac –, sehen Sie nichts. Das Programm hat sich verabschiedet. Der Fehlerbericht gibt aber einen Hinweis:

Realife Crash report.png

Wenn Sie die nummerierten Zeilen verfolgen: Bei Nr. 5 wurde der Window1.Open-Event gestartet. Dieser hat offensichtlich einen PushButton gepusht, und dann liefen die Dinge aus dem Ruder. Schauen wir mal nach, was im Window-Openevent steht:

Sub Open() Handles Open
 bInitialize.Push
End Sub

Also wurde Button bInitialize gedrückt. Dessen Action-Event gibt weitere Hinweise:

Sub Action() Handles Action
 mBoard = nil
 mBoard = new Board(val(tWidth.text), val(tHeight.text))
 mBoard.Initialize(val(tSeed.Text)/100)
 Redraw
End Sub

Irgendwo zwischen der Initialisierung von mBoard und dem Ende der Methode muss der Fehler sitzen. Der Constructor der Klasse Board als erste Anlaufstelle sieht auf den ersten Blick unverdächtig aus:

Public Sub Constructor(sX as Integer, sY as integer)
 // Create the array of cells
 redim Cells(-1,-1)
 redim Cells(sX-1, sY-1)
 
 // create each cell
 sX = SizeX-1
 sY = SizeY-1
 for y as integer = 0 to sY
  for x as integer = 0 to sX
   Cells(x,y) = new Cell(me, x, y)
  next
 next
 
 // initialize the array
 Initialize
End Sub

Ob im Constructor von Cells ein Problem steckt?

Public Sub Constructor(b as Board, x as Integer, y as integer)
 // Keep a refrence to the board the cell is on, and where on the board it is
 mBoard = b
 mX = x
 mY = y
End Sub

Völlig sauber! Hier werden nur Werte übergeben, die Klassenobjekten oder ihren Properties entsprechen.

Board.Initialize?

Public Sub Initialize(LifeProb as double = 0.25)
 // Initialize each cell
 dim sX as Integer = SizeX-1
 dim sY as Integer = SizeY-1
 for y as integer = 0 to sY
  for x as integer = 0 to sX
 // Initial State
   Cells(x,y).Initialize(LifeProb)
  next
 next
 mGeneration = 0
End Sub

Setzt offenbar nur Startwerte und initialisiert die Zellen. Also Cell.Initialize?

Public Sub Initialize(LifeProb as double = 0.25)
 // Initialize the current state
 mCurrentState = (Rnd <= LifeProb)
End Sub

Setzt sogar nur einen Boolean – das ist alles 64 Bit-unkritisch, also Fehlanzeige!

Dann also die Draw-Methode als letzte Möglichkeit. Die beginnt mit

 // Set the screen scaling factor
 if ScreenScalingFactor(g) > 1 then
  mIsRetina = true
 else
  mIsRetina = false
 end if

Was ist ScreenScalingFactor? Eine eigene Methode des Projekts!

Public Function ScreenScalingFactor(g as graphics) as single
 // Retina Support for Mac OS X
 
 #if TargetCocoa then
  Soft Declare Function CGContextConvertRectToDeviceSpace Lib "ApplicationServices" (c as integer, inRect as CGRect) As CGRect
  Dim sourceRect as CGRect
  sourceRect.width = 100
  sourceRect.height = 100
 
  Dim repRect as CGRect = CGContextConvertRectToDeviceSpace( g.Handle( g.HandleTypeCGContextRef ), sourceRect )
 
  return repRect.width / sourceRect.width
 #else
  #Pragma Unused g
  return 1.0
 #endif
End Function

Diese Projektversion stammt noch aus Zeiten vor der offiziellen Retina-HiDPI-Unterstützung, und für den Mac wurde diese Funktion per Declare nachgerüstet. Dabei benutzt wurde die Methode

CGContextConvertRectToDeviceSpace

die wir uns in Apples Dokumentation ergooglen können.

Diese Funktion nimmt (u.a.) ein CGRect entgegen, die macOS-eigene Darstellung eines Rechtecks, einer Struktur zur Beschreibung eines Rechtecks vergleichbar mit Xojo.Core.Rect. Schlagen wir dieses wiederum nach, sehen wir, dass diese Struktur die Felder Origin und Size beinhaltet – ersteres selbst eine Struktur des Typs CGPoint, letzteres der Gattung CGSize. Und folgt man auch deren Beschreibung (jaja, ich weiß: Das könnte auch übersichtlicher sein!), findet man, dass diese aus je zwei Datentypen der Art CGFloat zusammengesetzt sind.

CGFloat ist ein mac-exklusiver Datentyp: Auf einem 32 Bit-System entspricht er einem Single, auf einem 64 Bit-System einem Double. Seit einiger Zeit existiert CGFloat auch in Xojo.

Nehmen Sie das Projekt unter die Lupe, finden Sie CGRect als Struktur der Klasse Board. Dort allerdings so:

cGRect.png

Also mit Singles statt CGFloat deklariert, und deshalb funktioniert dieser Declare unter 64 Bit nicht. Was einfach zu beheben ist:

  • Ändern Sie die vier Werte von CGRect in den Typ CGFloat, indem Sie ihre Definitionen überschreiben, also
X As CGFloat

usw.

  • Jetzt kompilieren Sie das Programm wieder und lassen es ausführen: Besser, oder?

Strenggenommen können wir jetzt gleich weiter optimieren: Xojo kann ja nativ HiDPI. Also können Declare-Methode und Struktur ganz gestrichen werden und systemübergreifende Kompatibilität durch BitMapForCaching statt new Picture erreicht werden. Dazu speichere ich das Fenster als Xojo.Core.WeakRef, um auf seinen ScalingFactor zugreifen zu können.

Zusammen mit einer kleinen Optimierung der Cell.Evolve-Methode freut sich damit nicht nur der Windows-Bildschirm über bessere Grafiken: Schneller wird alles auch noch ein Stückchen. Die Änderungen im Detail finden Sie in der überarbeiteten Projektdatei.

CGFloat-Problematiken werden Sie nur unter macOS finden. Weiterhin im Zusammenspiel mit Declares sind Integer kritisch zu beäugen. Auch hier müssen Sie die Dokumentation genau studieren, um herauszufinden, ob nun wirklich ein Integer (der auf 32 Bit-Systemen 4 und auf 64 Bit-Systemen 8 bit groß ist) benötigt wird oder in Wirklichkeit eine spezialisierte Unterart, etwa Int32. Wenn’s unter 32 Bit läuft und unter 64 Bit nicht, ist die Wahrscheinlichkeit groß, dass ein Declare zu sehr auf die Unveränderbarkeit der 32 Bit vertraute.

 

 

 

 

 

2 Gedanken zu “Alte Projekte modernisieren – Teil II

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s