C++Guns – RoboBlog blogging the bot

20.12.2014

Fortran COMMON is deadly

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

In Fortran 77 gibt es den COMMON Block. Das sind sowas wie globale Variablen die man umbenennen kann. Gleich im nächsten Standard (1990) hat man das wieder rausgenommen, weil es eine unheimliche Scheisse ist. (Die Erkenntnis hat ja nur 13 Jahre gedauert).

Folgenden Beispiel zeigt warum.


      SUBROUTINE PREVENTSTART
        COMMON /ROCKETCONTROL/ ISTARTCOUNTDOWN, ITIMELEFT

        ! stop the countdown and reset time
        ISTARTCOUNTDOWN = 0
        ITIMELEFT = 1000
      END SUBROUTINE

      PROGRAM ROCKET

        COMMON /ROCKETCONTROL/ ITIMELEFT, ISTARTCOUNTDOWN

        ! initital values
        ITIMELEFT = 1000
        ISTARTCOUNTDOWN = 0

        ! now we start the countdown
        ISTARTCOUNTDOWN = 1

        ! some countdown loop here
        ! 999
        ! 998
        ! 997
        ! ...
        
        ! Oh no! Stop the countdown
        CALL PREVENTSTART

        IF(ITIMELEFT == 0) THEN
          WRITE(*,*) "ROCKET STARTED, YOU ARE DEAD!"
        ELSE
          WRITE(*,*) "COUNTDOWN STOPPED!"
        ENDIF
      END PROGRAM
$ gfortran -Wall rocket.for -o rocket
$ ./rocket 
 ROCKET STARTED, YOU ARE DEAD!

Shit ;) Ok die Erkenntnis hat gefruchtet. Man hat den Fortran Standard nicht umsonst schon 4 mal geändert. Damit ihr auch versteht wie es richtig geht, hier ein Beispiel im Fortran 2003 Standard.


module RocketControlClass
  implicit none
  private

  type, public :: RocketControl
    integer, private :: timeLeft
    integer, private :: countdownIsRunning

    contains

    procedure :: startCountdown
    procedure :: preventStart
    procedure :: getTimeLeft
  end type

  interface RocketControl
    module procedure createRocketControl
  end interface

  contains

  function createRocketControl() result(this)
    implicit none
    type(RocketControl) :: this

    this%timeLeft = 1000
    this%countdownIsRunning = 0
  end function

  subroutine startCountdown(this)
    implicit none
    class(RocketControl), intent(inout) :: this

    this%countdownIsRunning = 1
  end subroutine

  subroutine preventStart(this)
    implicit none
    class(RocketControl), intent(inout) :: this

    this%countdownIsRunning = 0
  end subroutine

  pure function getTimeLeft(this) result(timeLeft)
    implicit none
    class(RocketControl), intent(in) :: this
    integer :: timeLeft

    timeLeft = this%timeleft
  end function
end module

program rocket
  use RocketControlClass
  implicit none

  type(RocketControl) :: control

  ! inititalise values
  control = RocketControl()

  ! now we start the countdown
  call control%startCountdown

  ! some countdown loop here
  ! 999
  ! 998
  ! 997
  ! ...

  ! Oh no! Stop the countdown
  call control%preventStart

  if(control%getTimeLeft() == 0) then
    write(*,*) "rocket started, you are dead!"
  else
    write(*,*) "countdown stopped!"
  endif
end
$ gfortran -std=f2003 -Wall rocket.f03 -o rocket2003
$ ./rocket2003 
 countdown stopped!

Gott sei Dank. Diesmal hat es funktioniert, wir leben noch.
Für alle die immernoch nicht kapiert haben warum der 77er Code nicht funktioniert: Ich habe die Variablennamen im COMMON Block vertauscht. Einmal ISTARTCOUNTDOWN, ITIMELEFT und einmal ITIMELEFT, ISTARTCOUNTDOWN. Das ist kein Fehler, das ist im Standard so vorgesehen. Kein Compiler und kein forcheck wird diesen Fehler jemals erkennen können. Scheisse, ich sag es ja.

Kommen wir nun zum 2003er Code. Der ist erstmal deutlich länger. Aber das ist Absicht um einige Konzepte zu zeigen die man für ein ordentlichen Programm so braucht.

Kommen wir gleich zum wichtigsten: Statt COMMON nutze ich TYPE. Ein type kann wie COMMON Variablen zusammen fassen. Aber er existiert nur einer einzigen Stelle im Code und die Variablennamen kann man nicht mehr umbenennen. Das alleine würde schon langen um den 77er Fehler zu erkennen.

Aber die Welt dreht sich weiter und seit 1995 gehts erst so richtig rund.

Ich habe den RocketControl type in ein RocketControlClass - Module ausgelagert. Da Fortran case-insensitive ist, also nicht zwischen GROSS und kleinschreibung! unterschieden wird, muss man selbst seine Variablennamen anpassen. Module bekommen den Suffix "Module" um deutliche zu machen, dass es ein Module ist. Habe ich ein Module welches Objektorientierten Code enthält, bekommt es stattdessen den Suffix "Class" in Anlehung an das class Keyword in C++. *uff* Fortran ist anstrengend.

Okay zurück zu unserem Programm. Ich habe den type in ein module ausgelagert, und normal lagert man jedes Module in eine eingene Datei aus. Dann bleibt die Übersicht erhalten und auch das Versionskontrollprogramm freut sich.

Durch das Modul ist es möglich den type RocketControl überall im Programm zu nutzen, ohne die Definition mit allen Variabeln erneut hinschreiben zu müssen. Damit verhindert man nicht nur etliche copy&paste Fehler. Wenn man den type RocketControl um einige Variablen ergänzt, muss nur eine Stelle im Code geändert werden. Mit COMMON müssten alle Stellen im Programm geändert werden, wo RocketControl genutzt wird. Und jedesmal läuft man Gefahr einen Fehler zu machen. Variablen vergessen oder zu vertauschen. Und wie gesagt, mit COMMON kann der Compiler uns nicht vor Fehler beschützen. Werden types benutzt, kommt man erst garnicht in eine Situaton um die erwähnten Fehler zu machen. Der Compiler erledigt den Rest für uns. Und er kann noch mehr!

Für alle die es nicht wissen: Mit dem use-keyword können Module benutzt werden. Z.B. "use RocketControlModule"

Das Benutzen von types in Modulen bietet noch weitere Vorteile. Angenommen wird würden unseren type RocketControl um die Subroutine setCountDown(value) erweitern, um anzugeben wie lange der Countdown läuft bis die Rakete startet. Die Subroutine nimmt genau ein Argument "value" entgegen der vom Type Integer sein soll.

Wenn wir nun einen Fehler machen, und wir sind Menschen, wir machen etliche Fehler... Wenn wir also der Subroutine setCountDown zwei Variablen übergeben, statt nur einer, was dann? Ohne Module sagt der Compiler garnichts. Im günstigsten Falle gibt es ein Warning.
Das Programm würde laufen, aber könnte auch Abstürzen.

Das ist doch schön. Wir haben ein offensichtlich falsches Programm, welches keine Compilier und auch keine Laufzeitfehler zeigt. Das ist eine tickende Zeitbombe! Irgendwann wechseln wir den Compiler, oder wecheln den Rechner, oder schalten eine Compileroptimierung dazu und BUMM Segfault beim Funktionsaufruf. Na Geil. Das kann doch kein normaler Mensch debuggen. Vorallem nicht nach 10 20 Jahren.

Also? Genau, seine Funktionen in Module packen. Ich darf das mal demonstieren:

rocket.f03:71.32:

  call control%setCountDown(1,2)
                                1
Error: More actual than formal arguments in procedure call at (1)

Ja ist es denn Wahr, wir haben schon wieder die Welt gerettet.
Zur Erklärung warum Module die Welt retten: Sieht der Compieler ein Modul, legt er eine .mod Datei an. In unserem Fall würde sie rocketcontrolclass.mod heissen. Benutzt man den gfortran kann man diese Datei sogar mit einem einfachen Texteditor öffnen und sich ansehen. Man sieht in kodierter Form zu jede Funktion ihre Eigenschaften. Wieviel Parameter sie entgegen nimmt und vom welchen Type sie sind.
Mit einem Module kann der Compiler also sehr einfach unseren Fehler entdecken. Und genau das ist doch eine der Hauptaufgaben von Compiler. Neben der Umwandlung von Text in Binärcode muss er schauen, ob das alles Sinn macht was wir programmiert haben. Meistens stimmt es nicht...

Ich möchste dieses Beispiel benutzen um einen weiteren beliebten Fehler zu zeigen.
Angenommen der Benutzer unseres tollen Programms setzt nun CountDown von 5.5 Sekunden. Er übergibt also eine Gleitkommazahl vom Type Real einer Funktion, die einen Integer erwartet. Ohne Module sagt der Compiler nichts, bestensfalls ein Warning.
Beim Ausführen wird das Bitmuster der Real Zahl als Integer Zahl interpretiert. Dabei kann alles mögliche herauskommen. Sehr größe Zahlen die die Rakete erst nächstes Jahr starten oder negative Zahlen oder gar Null. "ROCKET STARTED, YOU ARE DEAD!" Schonwieder... Nicht so gut.

Also Module retten mal wieder die Welt. Hier die passende Fehlermeldung:

rocket.f03:71.28:

  call control%setCountDown(1.0)
                            1
Error: Type mismatch in argument 'value' at (1); passed REAL(4) to INTEGER(4)

Sehr schön. Der Compiler hat uns wieder gerettet.
Wir schreiben also dem Entwickler dass sein Programm scheisse ist, und es gefälligst auch Kommazahlen unterstützen soll. Der fleissige Entwickler schickt uns sofort die neue Version zu und...

Error: Type mismatch in argument 'value' at (1); passed REAL(8) to REAL(4)

Verdammt! Was ist denn nun schon wieder? In Fortran können 64Bit Zahlen nicht ohne weiteres in 32Bit Zahlen umgewandelt werden. Das ist auch richtig so. Man würde wertvolle Informationen bei der Umwandlung verlieren und gerade Zahlengenauigkeit ist in der Wissenschaft sehr wichtig. An dieser Stelle muss ich sagen: "Gut gemacht, Fortran Entwickler!" Genießt es, es kommt nicht noch einmal vor.
Wie löst man das Problem? Auf die richtige Art und Weise? Wir speichern in RocketControl 64Bit Zahlen und geben dem Benutzer die möglichkeit 64Bit oder 32Bit Zahlen einzugeben. Mit Function overloading geht das sehr elegant und die Umwandlung von 32Bit nach 64Bit geht verlustfrei.

Module können gut die Welt retten. War es das jetzt? Ha! Noch lange nicht. Was ist mit diesem Stück Code?


control%timeLeft = 1.0

Anders als bei Funktionsaufrufe wo man den Stack kaputt machen kann, ist hier die Umwandlung ohne Error Möglich. Man bekommt höchstens ein Warning

  control%timeLeft = 1.0
                     1
Warning: Possible change of value in conversion from REAL(4) to INTEGER(4) at (1)

Ja, was nutzen uns all die Funktionen im Module, wenn der Programmierer sie nicht benutzt und stattdessen direkt auf die Variable zugreift. Es sollte jedem einleuchten, dass die Welt wieder verloren ist.

Glücklicherweise wurde der Fortran Standard nachgerüstet und man kann type Variablen als private markieren.


  type, public :: RocketControl
    integer, private :: timeLeft
  end type

Per Default ist "public" vorgegeben, so dass die Variable gelesen und geschrieben werden kann. Gibt man "private" vor, kann die Variable nur noch von Procedure genutzt werden, die RocketControl enthält. Versucht man von ausserhalb auf die Variable lesend oder schreiben zuzugreifen, erhält man einen Compiler Fehler:

rocket.f03:71.18:

  control%timeLeft = 1
                  1
Error: Component 'timeleft' at (1) is a PRIVATE component of 'rocketcontrol'

Und die Welt ist wieder sicher. Geschützt vor un/absichtlichen Zugriffen von Programmierer die keine Ahnung haben was sie tun. Mein Superheld der Module-Compiler.

Wir zwingen den Benutzer also die setCountDown Funktion zu benutzen. Und es ist zu 100% sichergestellt, dass der Compiler mit Modulen die gezeigten Fehler erkennt. Jetzt können wir auch folgende Situation gut lösen:


call control%setCountDown(-1)

Negative Werte machen für einen Countdown natürlich keinen Sinn. Wir können in der Funktion den übergebenen Wert auf Gültigkeit überprüfen, eine Fehlermeldung ausgeben und den neue Wert nicht akzeptieren. Da der Benutzer die Funktion setCountDown() aufrufen MUSS, ist auch sichergestellt dass die Überprüfung stattfinden.

Und wir können uns entspannt zurück lehnen und sicher gehen, dass auch in den nächsten 10 Jahren niemand unser Programm falsch benutzen kann. Selbst wenn er es will. Compiler+Module+neuen Standard sei Dank.

Anmerkung:
Wie bereits erwähnt ist "public" default in Fortran. Bei C++ ist es "private" für Klassen. Fortran lebt also in dem Glauben, dass die Programmierer immer alles richtig machen und niemand un/absichtlich eine Variable falsch benutzt.
In C++ ist die Welt schwarz, jeder hat nur böses im Sinn, niemand kann man trauen, alles muss kontrolliert werden. Wenn wir mathematische Modulle implementieren, welche ein Teil der Umwelt abbilden, welcher Ansatz wäre da wohl realistischer? ;)

16.11.2014

2qm 2014 Teil 1

Filed under: Allgemein — Tags: , — Thomas @ 21:11

Es ist soweit, Eisenbahnzeit.
Eine zwei Quadratmeter Bahn soll es werden. Mit langen Bahnhof für die D-Zug Waggons, Pendelzug Stecke und ein paar Abstellgleise. Die Landschaft mit Dorf und Berg darf natürlich auch nicht zu kurz kommen.

Den Tisch vom letzten Jahr benutzen wir weiter. Sowie die Grundplatte, die wir aber leider auf 103x200cm kürzen mussten.
Es ist schwierig aber nicht unmöglich eine anspruchvolle Bahn auf 2qm unter zu bringen. Der erste Versuch mit zwei Kehrschleifen würde funktionieren, aber dann bleibt einfach kein Plaz mehr für Abstellgleise oder gar die Pendelzugstrecke.

Also musste als Grundform wieder der Kreis herhalten. Hier der vorläufige Gleisplan

2qm2014_16.11_2

2qm2014_16.11_0

2qm2014_16.11_1

Spass mit Pointer

Filed under: Allgemein — Tags: , — Thomas @ 20:11

"man kann sich so derbe den kopf weg schiessen damit"


int global;

char *func() {
  return (char*)&global;
}

int main() {

  char *x = func();
  int *RocketPtr = (int*)x;
  *RocketPtr = 1337;
  cout << global;
}

Output : 1337

13.11.2014

Märklin M 4fach Wendekreis

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

Nach ein paar mir zugeschickten Anregungen für einen Wende/Durchfahrkreis mit Chaos Funktion, hier nur meine Version.
Um es einfacher zu machen, herrscht hier Rechsverkehr und der Kreis darf nur im Gegenuhrzeigersinn durchfahren werden. Daneben gibt es noch eine Umgehungsmöglichkeit für den Kreisel, damit sich mehrere Loks nicht in die Quere kommen.
Es ist also Möglich von jeder Seite aus einzufahren und aus jeder Seite wieder auszufahren.

kreis

Anbei die WinTrack Datei kreis.zip

Stückliste
16x	Mä5106	Gleis gerade 1/1 Länge 180 mm
4x	Mä5107	Gleis gerade 1/2 Länge 90 mm	
4x	Mä5108	Gleis gerade 1/4 Länge 45 mm
12x	Mä5110	Gleis gerade 1/8 Länge 22.5 mm
8x	Mä5205	Gleis gebogen Ergänzungs- stück
4x	Mä5100	Gleis gebogen 1/1 Radius 360 mm Normalkreis	
16x	Mä5101	Gleis gebogen 1/2 Radius 360 mm Normalkreis	
4x	Mä5202L	Weiche links Radius 437.4 mm Parallelkreis	
4x	Mä5202R	Weiche rechts Radius 437.4 mm Parallelkreis	
4x	Mä5140L	Kurvenweiche links	
4x	Mä5140R	Kurvenweiche rechts	

25.10.2014

Note Videospiele

Filed under: Allgemein — Thomas @ 10:10

http://www.heise.de/newsticker/meldung/Videospielen-in-3D-intensiviert-den-Zorn-2431744.html

20.10.2014

howto allocate array in C++ and pass it back to fortran

Filed under: Allgemein — Tags: , , — Thomas @ 12:10

test.cpp


#include < stdlib.h >

// fortran can only call C routines
extern "C" {
  // we need the address of the fortran pointer thus a pointer of a pointer.
  void myC_func(int **arr) {   
    // we cannot use new() here because fortran deallocate() use free() instead of delete[]
    *arr = (int*) malloc(12*sizeof(int));
    for(int i=0; i < 12; i++) {
      (*arr)[i] = i;
    }
  }
}

test.F90


! https://gcc.gnu.org/onlinedocs/gcc-4.6.2/gfortran/Working-with-Pointers.html

program main
  use iso_c_binding
  implicit none
  
  interface
    subroutine my_routine(arr) bind(c,name='myC_func')
      import :: c_ptr
      ! If a pointer is a dummy-argument of an interoperable procedure, 
      ! it usually has to be declared using the VALUE attribute. 
      ! void* matches TYPE(C_PTR), VALUE, while TYPE(C_PTR) alone matches void**. 
      type(c_ptr), intent(out) :: arr
    end subroutine
  end interface
  
  type(c_ptr) :: cptr
  integer,pointer :: fptr(:)
  
  ! allocate and fill cptr
  call my_routine(cptr)
  ! convert it to fortran pointer
  call c_f_pointer(cptr, fptr, [12])
  
  if(associated(fptr)) write(*,*) "associated"
  write(*,*) fptr
  deallocate(fptr)
  
end program main
g++ -Wall -Wextra -c test.cpp 
gfortran -Wall -Wextra -fcheck=all test.F90 test.o

valgrind ./a.out 
==5256== Memcheck, a memory error detector
==5256== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==5256== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==5256== Command: ./a.out
==5256== 
 associated
           0           1           2           3           4           5           6           7           8           9          10          11
==5256== 
==5256== HEAP SUMMARY:
==5256==     in use at exit: 0 bytes in 0 blocks
==5256==   total heap usage: 22 allocs, 22 frees, 11,874 bytes allocated
==5256== 
==5256== All heap blocks were freed -- no leaks are possible
==5256== 
==5256== For counts of detected and suppressed errors, rerun with: -v
==5256== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

14.10.2014

eneloop pro 2450mAh Akku Erfahrungsbericht

Filed under: Allgemein — Thomas @ 23:10

Es wurde Zeit für ein Satz neuer Akkus für die Farradlampe. Drei von vier Akkus haben nur noch die hälfte an Kapazität und einer ist ganz hinnüber (nachdem er mir mal in die Sandbach gefallen war).

Also bin ich zum Batterie Kaiser gefahren und hab mir 4 neue geholt. Für 20Eur. Macht 5Eur das Stück. Für den Preis müssen die Akkus mich aber überzeugen. Das meinte auch die Verkäuferin. Die eneloop Akkus sollen eine waagerechte Entladekurve haben. Da da bin ich mal gespannt.

Ich werde einige Lade- und Entladekurven mit unterschiedlichen Ström aufzeichnen.

Eine genaue Angabe auf der Panasonic Webseite wie lange und mit welchem Strom die Akkus geladen werden, habe ich nicht gefunden. Die Passenden Ladegeräte brauchen etwa 7h für 2000mAh, wenn ich die Angaben richtig gedeutet habe. Das macht 285mA also werde ich mit 300mA laden. Mit 400mA ging es bestimmt auch.

Hier ist die dritte Entladekurve. Sie fällt schön flach ab. Man bräuhte mal einen anderen Akku zum vergleichen...

Dritte Entladekurve mit 0.6A

Dritte Entladekurve mit 0.6A

Mit Solarstrom vorgeladen.
Nette Idee. Mit einem Entladestrom von 0.6A kamen noch 1100mAh aus dem Akku. Also lässt sich der Akku durchaus ohne vorheriges Laden verwenden.

Entladekurve eines frisch gekauften Akkus

Entladekurve eines frisch gekauften Akkus


...

07.10.2014

Faster Code – Part 6 – Sprungvorhersage - anschaulich

Filed under: Allgemein — Tags: , , — Thomas @ 12:10

Sehr schöne anschauliche Erklärung:

http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array

04.10.2014

merge/connect nearby poylgons

Filed under: Allgemein — Tags: , — Thomas @ 13:10

Zwei Polygone verbinden die sich überlappen ist einfach (union).
Aber was, wenn sich die Polygone nur berühren oder einen sehr kleinen Abstand von einander haben?

Im Internet findet man kaum etwas. Hier findet man einen Ansatz über die Konvexe Hülle. Ich hab ihn mir noch nicht genauer angesehen.

Mein Ansatz basiert darauf, die Polygone so zu verbinden, wie man das als Mensch auch machen würde. Also an den Stellen, wo sie sich zu nahe kommen, werden sie verbunden. Und die restlichen, dann überflüssigen, Teile vom Polygon gelöscht.

Um zu verdeutlichen was genau gemeint ist und wie die Ergebnisse aussehen, hier ein kleines Video.

Die jetzige Version funktioniert recht stabil. Es gibt noch zwei Sonderfälle die zu behandeln sind. Von der Komplexität her hat der Algorithmus eine quadratische Laufzeit und kann Theoretisch auf linear-logarithmisch gesenkt werden.
Aber erstmal werde ich überprüfen, ob der Algorithmus auch auf realen Daten zufriedenstellende Ergebnisse liefert.

12.09.2014

Python Naming Conventions

Filed under: Allgemein — Thomas @ 09:09

The naming conventions of Python's library are a bit of a mess, so we'll never get this completely consistent

http://legacy.python.org/dev/peps/pep-0008/

Python ist SCHROTT ;)

« Newer PostsOlder Posts »

Powered by WordPress