Funktionszeiger für B3D
Übersicht

![]() |
NoobodyBetreff: Funktionszeiger für B3D |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 ![]() |
||
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 |
![]() |
Xaymarehemals "Cgamer" |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
Bist du sicher Noobody, auch die Statischen Funktionen? | ||
![]() |
Silver_Knee |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 ![]() |
||
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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 |
![]() Antworten mit Zitat ![]() |
---|---|---|
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. |
||
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group