Funktionszeiger für B3D

Übersicht BlitzBasic DLLs und Userlibs

Neue Antwort erstellen

Noobody

Betreff: Funktionszeiger für B3D

BeitragMi, Mai 06, 2009 21:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Während den Arbeiten an meinem Lua - Wrapper stiess ich vor das grosse Problem, von Lua aus eine Funktion in Blitz aufzurufen. Um das zu ermöglichen, bräuchte man Funktionszeiger, die Blitz allerdings nicht unterstützt. Von den Umständen genötigt, machte ich mich daran, mit ein wenig C und Inline-Assembler Blitz die gewünschten Fähigkeiten aufzuzwingen.
Entstanden ist dabei diese Lib, die ich euch natürlich nicht vorenthalten will. Sie ermöglicht es, mit ein wenig unschönem Gehacke, Zeiger auf Funktionen zu holen und diese Funktionen per Zeiger aufzurufen. Aufgrund der Probleme, die ich dabei hatte, sind weder Parameter noch Rückgabewerte möglich - das müsste man daher über globale Variablen lösen.

Mit der DLL kann man allgemein einen Zeiger auf ein Stück Code holen. Das geht ganz einfach mit Code: [AUSKLAPPEN]
CodePointer = GetInstructionPointer()

Diesen Zeiger kann man dazu verwenden, später direkt zu dieser Stelle zu springen Code: [AUSKLAPPEN]
CallFunction( CodePointer )


Problematisch wird es dann, wenn man zum Aufrufer zurückkehren möchte. Tut man dies in einer Funktion mit Return, fliegt einem ganz Blitz3D um die Ohren - sollte man also tunlichst vermeiden. Allerdings funktioniert es, wenn man im Hauptcode per Return zurückkehr. Wie geht denn das?
Nun, das Return im Hauptcode ist an sich für Gosub gedacht, funktioniert aber perfekt mit der DLL. Der Code würde also jetzt so aussehen Code: [AUSKLAPPEN]
Global FunctionPointer

Gosub GetPointer ;Erstmals hinspringen, um den Zeiger zu holen
CallFunction( FunctionPointer ) ;Die Funktion per Zeiger aufrufen

.GetPointer
FunctionPointer = GetInstructionPointer() ;Den Zeiger holen
TestFunktion()
Return ;Und zurück zum Aufrufer

Function TestFunktion()
   Print "Dum di dum"
End Function


Damit können wir schon eine Funktion per Zeiger ansteuern, ohne dass uns Blitz gleich dabei draufgeht.
Das einzige, was jetzt noch nicht passt, ist, dass jetzt zweimal "Dum di dum" auf dem Bildschirm steht - logisch, das erste Mal, als wir den Funktionszeiger holten (da wurde die Funktion auch aufgerufen) und das zweite mal, als wir sie per CallFunction ansteuerten.

Das können wir schnell über eine globale Variable ändern Code: [AUSKLAPPEN]
Global FunctionPointer, FirstCall = True

Gosub GetPointer ;Erstmals hinspringen, um den Zeiger zu holen
CallFunction( FunctionPointer ) ;Die Funktion per Zeiger aufrufen

.GetPointer
Temp = GetInstructionPointer() ;Den Zeiger holen

If FirstCall Then
   FunctionPointer = Temp ;Beim ersten Aufruf wollen wir nur den Zeiger, ohne die Funktion tatsächlich aufzurufen
Else
   TestFunktion()
EndIf

FirstCall = False
Return ;Und zurück zum Aufrufer

Function TestFunktion()
   Print "Dum di dum"
End Function


Wie man sieht, benötigt man einiges an Gehacke, um die Zeiger zum funktionieren zu bringen, aber für manche Anwendungen ist es den Aufwand wert.
Die erhaltenen Zeiger muss man natürlich nicht in globalen Variablen speichern - man kann sie zum Beispiel auch in Type - Feldern oder Arrays speichern, je nach dem, in welcher Form dass sie benötigt werden (für GUIs oder Scriptsprachen ziemlich nützlich).

Im Downloadpaket ist die DLL sowie die Decls zu finden, ausserdem ist ein kleines Beispiel, welches das Ansteuern von mehreren Funktionen erklärt, sowie der Code der DLL beigelegt.
Download

Bei Fragen und Anregungen stehe ich selbstverständlich zur Verfügung Razz
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

Xaymar

ehemals "Cgamer"

BeitragMi, Mai 06, 2009 21:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Ginge das nicht auch, wenn man die bb exe als dll lädt? so hab ich es vorher immer gemacht.(calldll, allerdings in ner anderen sprache)

wenn das nicht geht, ist sowas sehr nice

[Edit]Nein geht nicht. :/ da freut man sich doch schon über das was noobody so macht:D
Warbseite

Silver_Knee

BeitragSa, Mai 09, 2009 17:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Soweit ich das jetzt verstanden habe ist:

Code: [AUSKLAPPEN]
.bla

Goto Bla


das gleiche wie

Code: [AUSKLAPPEN]
CodePointer = GetInstructionPointer()

CallFunction( CodePointer )


Und das problem ist dass man GetInstructionPointer() immer ausführen muss um den Pointer auch zu bekommen? Sind diese Pointer nach Applikationsende denn noch gültig? zB mit configdatei

Noobody

BeitragSa, Mai 09, 2009 17:49
Antworten mit Zitat
Benutzer-Profile anzeigen
Silver_Knee hat Folgendes geschrieben:
Soweit ich das jetzt verstanden habe ist:

Code: [AUSKLAPPEN]
.bla

Goto Bla


das gleiche wie

Code: [AUSKLAPPEN]
CodePointer = GetInstructionPointer()

CallFunction( CodePointer )

Von der Logik her, ja.
Allerdings kann man mit diesem Zeiger sehr viel mehr anfangen als mit einem Label - man kann ihn in Arrays und in Types stecken und selbst an externe DLLs übergeben, die Funktionszeiger verlangen.

SilverKnee hat Folgendes geschrieben:
Und das problem ist dass man GetInstructionPointer() immer ausführen muss um den Pointer auch zu bekommen?

Was ist denn das Problem dabei?

SilverKnee hat Folgendes geschrieben:
Sind diese Pointer nach Applikationsende denn noch gültig? zB mit configdatei

Nein.
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

kog

BeitragSa, Mai 09, 2009 18:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Bist du sicher Noobody, auch die Statischen Funktionen?

Silver_Knee

BeitragSo, Mai 10, 2009 1:22
Antworten mit Zitat
Benutzer-Profile anzeigen
naja ich sag mal: es wäre einfacher wenn man irgendwie vorher an die Adresse kommen würde.

Das hier als Alternative zu deinem Beispiel

Code: [AUSKLAPPEN]
;<Funktionen initiieren>
Local init=true
TestFunktion = GetInstructionPointer() ;Den Zeiger holen
TestFunktion()
If Not init
  Return ;Und zurück zum Aufrufer
EndIf
init=false
;</Funktionen initiieren>

CallFunction( TestFunktion ) ;Die Funktion per Zeiger aufrufen

Function TestFunktion()
   Print "Dum di dum"
End Function


Warum ist bei dir FunctionPointer global?

Silver_Knee

BeitragSo, Sep 13, 2009 21:36
Antworten mit Zitat
Benutzer-Profile anzeigen
Sry für den Doppelpost hat aber 1. nichts mehr mit meinem letzten post zu tun und holt 2. den thread somit wieder nach oben. Hoffe das geht in ordnung.

Deine DLL funktioniert leider nur beim ersten aufruf. danach läuft nix mehr:

Code: [AUSKLAPPEN]
Local proginit=1,lblCool,count

lblCool=GetInstructionPointer()
If Not proginit
   Print "cool"
   Return
EndIf

proginit=0

For count=1 To 10
   CallFunction lblCool
Next

WaitKey


zu erwarten wäre dass nun zehn mal cool auf dem Bildschirm steht. Es steht aber nur einmal da. Hast du (oder jemand anderes) ne idee woran das liegen könnte.
evtl eine idee:
Ich kann eig kein assambler aber soweit ich gelesen habe nimmt pop doch was vom stack runter... musst du es dann nicht wieder drauf tun oder liest es nur aus... du veränderst ja auch die lese position... hab immer gedacht man muss erst solange sachen vom stack nehmen bis man bei seiner wiunsch info ist^^

Noobody

BeitragSo, Sep 13, 2009 22:31
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn du den Code so benutzt, springt er beim ersten Aufruf per CallFunction direkt zur Zeile lblCool=GetInstructionPointer(). Damit wird der bisher gespeicherte Funktionszeiger überschrieben und beim nächsten Aufruf von CallFunction springt er an eine unbestimmte Position. Mach es daher so Code: [AUSKLAPPEN]
Local proginit=1,lblCool,count

Temp=GetInstructionPointer()
If proginit
   lblCool = Temp
Else
   Print "cool"
   Return
EndIf

proginit=0

For count=1 To 10
   CallFunction lblCool
Next

WaitKey


Mit dieser Methode wird der Funktionszeiger nur beim ersten Aufruf gespeichert, was auch der einzige richtige Weg ist.
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

Silver_Knee

BeitragMo, Sep 14, 2009 8:45
Antworten mit Zitat
Benutzer-Profile anzeigen
ideal wäre wenn du rausfinden würdest wann der befehl zu ende ist um dann nach dem befeh zurückzuspringen...
hatte aber auch scon die idee dass er die variable überschreibt.
trotzdem thx für die antwort die dll ist echt klasse

Noobody

BeitragMo, Sep 14, 2009 12:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Silver_Knee hat Folgendes geschrieben:
ideal wäre wenn du rausfinden würdest wann der befehl zu ende ist um dann nach dem befeh zurückzuspringen...

Das habe ich damals relativ lange probiert, bin aber auf keine Ergebnisse gekommen. Der Code der DLL liegt aber frei, falls also jemand eine Idee hat, nur zu Razz
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

Der Eisvogel

BeitragMo, Sep 14, 2009 13:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Also wenn man jetzt noch irgendwie Parameter dazu geben könnte wäre das cool.
Ungarische Notation kann nützlich sein.
BlitzMax ; Blitz3D
Win 7 Pro 64 Bit ; Intel Core i7-860 ; 8 GB Ram ; ATI HD 5750 1 GB
Projekte: Window-Crasher
Ich liebe es mit der WinAPI zu spielen.

Silver_Knee

BeitragMo, Nov 02, 2009 0:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Also ich hab grade rausgefunden:

Das ding was bei dem 2. und dritten aufruf in tmp gespeichert wird ist das nächste element auf dem stack, weil dort der Rückgabewert von GetInstructionPointer erwartet wird und man ja in die Wertzuweisung hineinspringt.

ich hab also mit einer c-Funkton:

Code: [AUSKLAPPEN]
 .... CallFunction(...BBFunktion(int),parameter){
BBFunktion(parameter)
}


es schaffen können dieses tmp Nutzbar zu machen. Ein Beispielcode
Code: [AUSKLAPPEN]
Local proginit=1,lblCool,count

Temp=GetInstructionPointer()
If proginit
   lblCool = Temp
Else
   Print Temp
   Return
EndIf

proginit=0

For count=1 To 10
   CallFunction lblCool,count
Next

WaitKey

Kommt tatsächlich so weit eine 1 auf den bildschirm zu bringen. Leider mault ein Visualc++ error bei eingeschaltetem Debugger: Der speicher ist nicht aufgeräumt.
Tatsache. Jetzt hab ich einen parameter übergeben und der wird denke ich nicht vom stack geräumt nach der Prozedur. inline asm in BB gibts ja nicht aber vllt hast du ne idee das via c funktion zu regeln.

Neue Antwort erstellen


Übersicht BlitzBasic DLLs und Userlibs

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group