Montag, 7. November 2011

Ein "paralleler" Zugang

Parallel zum Testframework habe ich begonnen, mir das Include sweph.c vorzuknöpfen, das den Löwenanteil der Funktionalität für die Swiss Ephemeris enthält: Es gibt nur Absprünge in die Moshierfunktionen (swemmoon, swemplan), wenn Moshier explizit angefordert wurde oder das Datum aus dem Range der Swiss Ephemeris herausreicht, einige allgemeine Bibliotheksfunktionen (swephlib) und einen Aufruf von swe_revjul (swedate) bei der Namensbildung der Ephemeridendatei. Alles andere, was zur Berechnung eines Planeten mit Swiss Ephemeris nötig ist, ist in sweph.c enthalten.

Ich habe mir daher sweph.c in ein neues Programm test_calc_reduce.c hineinkopiert, eine main()-Funktion vorangestellt und im Makefile die Compilierung ohne Verwendung von sweph.o definiert:


test_calc_reduce.exe: test_calc_reduce.o
$(CC) $(CFLAGS) -DUSE_DLL -o test_calc_reduce.exe test_calc_reduce.o
swemmoon.o swemplan.o swephlib.o swedate.o
test_calc_reduce.o: swephexp.h swedll.h sweodef.h test_calc_reduce.h

Nun arbeite ich mich von oben durch die Funktionsebenen durch, um die Programmideen klarer hervortreten zu lassen und die Abhängigkeiten mit globalen Daten zu entfernen oder zu untersuchen. Wichtig sind auch hier Tests, um nach jeder (auch kleinen) Refaktorisierung sicherzustellen, dass die grundlegenden Aufrufe alle noch funktionieren. Im Moment mache ich das noch von Hand, plane aber einen Querschnittstest, der für jeden mögliche Berechnungstyp ein oder zwei Beispiele enthält. Eine interessante Aufgabe.

Die switch/cases, Sprungmarken und goto's sind, ebenso wie Kommentarblöcke, die mit einer langen Reihe von Sternchen beginnen, Hinweise darauf, dass man die Lesbarkeit verbessern kann, wenn man den nachfolgenden Code in eine parametrisierte Funktion auslagert. Der Stack ist ein grosser Helfer: Er hilft, die Geltung von Variablen auf kleinere Codeblöcke zu begrenzen. Sie werden einem zu Beginn der Routine automatisch auf- und beim Verlassen automatisch abgebaut. Änderungen an Parametern nimmt die Funktion wegen der Wertübergabe nur vor, wenn ich das explizit wünsche. Ansonsten sind nach dem Rücksprung die Daten automatisch in dem Zustand, den sie vor dem Aufruf hatten. Auch das ist ein hilfreiches Systemverhalten.

Die verschiedenen Arten der Berechung - z.B. Moshier-Mond, Asteroid, Ekliptikschiefe, habe ich in einzelne Funktionen der jeweils gleichen Signatur ausgelagert. Nach dem Strategiemuster vorgehend, kann ich aus iflag und ipl in einer separaten Funktion die Funktionsreferenz ermitteln, die für die Berechnung zuständig ist. Dadurch vereinfacht sich der Code der aufrufenden Funktion (swecalc()) erheblich. Es wird nicht sehr viel weniger Code, aber was übrig bleibt, ist klarer einzelnen Aufgaben zuzuordnen.

Was die globalen Daten angeht, so scheint das meiste in der globalen Struktur swed enthalten zu sein. Intern wird neu allen Funktionen, die auf swed zugreifen, ein Zeiger auf eine Struktur vom Typ swe_data übergeben, und die Zugriffe erfolgen über diesen Zeiger. So bereite ich vor, dass diese Daten im Stack verwaltet werden können statt im globalen Datenbereich.

So schlecht das Schlüsselwort static für Variablen ist, weil es neue versteckte globale Daten einführt, so hilfreich ist es für die Kapselung, weil es die Sichtbarkeit von Variablen und Funktionen auf ein File einschränkt. Eine mit static deklarierte Variable ist immer noch besser als eine überall sichtbare globale Variable. In dieser Hinsicht ist der static-Qualifier vergleichbar dem private in objektorientierten Sprachen.

Keine Kommentare:

Kommentar veröffentlichen