Ich habe eine kleine Skriptsprache entwickelt und gerade angefangen, die ersten nativen Bibliotheksbindungen zu schreiben. Dies ist praktisch das erste Mal, dass ich eine native Erweiterung einer Skriptsprache schreibe, daher bin ich auf ein konzeptionelles Problem gestoßen.
Ich möchte Klebercode für gängige Bibliotheken schreiben, damit diese in dieser Sprache verwendet werden können. Aufgrund des Designs der von mir geschriebenen Engine wird dies mithilfe eines Arrays von struct
Cs erreicht, das den Funktionsnamen beschreibt, der von der angezeigt wird virtuelle Maschine, zusammen mit einem Funktionszeiger.
Eine native Bindung ist also eigentlich nur eine globale Array-Variable, und jetzt muss ich ihr offensichtlich einen (vorzugsweise guten) Namen geben. In C ist es idiomatisch, eigene Funktionen in einen "Namespace" zu setzen, indem Funktionsnamen wie in myscript_parse_source()
oder ein benutzerdefiniertes Präfix vorangestellt werdenmyscript_run_bytecode()
. Der benutzerdefinierte Name soll idealerweise den Namen der Bibliothek beschreiben, zu der er gehört. Hier entsteht die Verwirrung.
Angenommen, ich schreibe eine Bindung für libcURL
. In diesem Fall erscheint es sinnvoll, meine Erweiterungsbibliothek folgendermaßen aufzurufen curl_myscript_binding
:
MYSCRIPT_API const MyScriptExtFunc curl_myscript_lib[10];
Aber jetzt kollidiert dies mit dem curl
Namespace. (Ich habe sogar darüber nachgedacht, es aufzurufen, curlmyscript_lib
aber leider verwendet libcURL nicht ausschließlich das curl_
Präfix - die öffentlichen APIs enthalten Makros wie CURLCODE_*
und CURLOPT_*
, daher gehe ich davon aus, dass dies auch den Namespace überladen würde.)
Eine andere Möglichkeit wäre, es als zu deklarieren myscript_curl_lib
, aber das ist nur gut, solange ich der einzige bin, der Bindungen schreibt (da ich weiß, was ich mit meinem Namespace mache). Sobald andere Mitwirkende beginnen, ihre eigenen nativen Bindungen hinzuzufügen, überladen sie jetzt den myscript
Namespace. (Ich habe einige Nachforschungen angestellt, und es scheint, dass zum Beispiel die Perl-cURL-Bindung diesem Muster folgt. Ich bin mir nicht sicher, was ich darüber denken soll ...)
Wie schlagen Sie vor, dass ich meine Variablen benenne? Gibt es allgemeine Richtlinien, die befolgt werden sollten?
SomeLang_register_extension("curl", "H2CO3", "000.001.000", the_extension)
wennthe_extension
auf eine Erweiterungsstruktur verwiesen wird, diese jedoch global nicht sichtbar ist. Ihre Sprache würde dann eine interne Datenstruktur beibehalten, um die Erweiterungen nach Name, Version und Autor zu sortieren.Antworten:
Realistisch gesehen ist es nicht erforderlich, ein globales Array exportierter Funktionen zu enthalten, um sie für Ihre Laufzeitumgebung sichtbar zu machen.
Eine gute Frage ist, ob Ihre Laufzeitumgebung den Funktionszeiger und den damit verbundenen Namen speichert.
Ihre Vorstellung, ein globales Array zum Exportieren von Funktionen zu verwenden, ist nur dann sinnvoll, wenn Ihre Laufzeitumgebung diese Informationen nicht speichert. Ich gehe jedoch davon aus, dass Sie sie speichern müssen, damit die Ausführungsumgebung der VM ordnungsgemäß ausgeführt werden kann.
Eine allgemeine Funktion, die einer Skriptsprache wie Ihrer hinzugefügt wird, ist die Idee , Ihre Funktionen zu registrieren .
Im Wesentlichen benötigen Sie ein Medium zum Speichern der exportierten Funktion ptr und des Namens des exportierten Funktionszeigers, die im Skriptcode verwendet werden.
Ich kenne zwei Optionen, die Sie dafür implementieren könnten:
Eine Hashmap, die den skriptseitigen Funktionsnamen als Zeichenfolgentaste und die Funktion ptr als Wert für diese Taste verwendet.
exported_funcs["do_thing"] = &native_do_thing;
In Skriptsprachen für eingebettete Systeme wie Pawn häufig verwendet, können Sie die Skriptdaten selbst zum Speichern des Funktionszeigers verwenden. Wenn Sie die Funktion "registrieren", iterieren Sie die exportierte Funktionstabelle des Skripts und suchen nach dem Namen, unter dem die exportierte Funktion registriert wird. Wenn Sie diesen Namen gefunden haben, überschreiben Sie diese Skriptdaten (nicht die Skriptdatei selbst, sondern die geladenen Skriptspeicherdaten), und Sie haben im Wesentlichen den nativen Funktionszeiger gespeichert.
FuncPtrType **table_entry = get_script_entry_ptr(func_name); *table_entry = func_ptr;
Ich rate dringend, dass Sie für die zweite Option dem Tabelleneintrag genügend Größe geben, um Zeigergrößen aller Architekturen aufzunehmen, insbesondere sollte der Eintrag 8 Byte betragen.
quelle