Sie wissen vielleicht schon alles, aber das ist ein bisschen eine FAQ, also werde ich ein paar Details schreiben.
Zunächst wollen wir verstehen, was ein Delegat ist. So wie ein Slice nur ein C-Datenzeiger ist, der mit einer Länge gepaart ist, ist ein Delegat nur ein C-Datenzeiger, der mit einem Funktionszeiger gepaart ist. Diese werden zusammen auf Funktionen sie erwarten, als ob sie definiert wurde
struct d_delegate {
void* ptr; // yes, it is actually typed void*!
T* funcptr; // this is actually a function pointer
};
(Beachten Sie, dass die Tatsache, dass es nur eine Daten ptr da drin ist der Grund für einige Compiler-Fehler, wenn Sie versuchen, eine verschachtelte Delegierten zu nehmen ! innerhalb einer Klasse-Methode)
die void*
ist, was mit den Datenpunkten und wie mit einer Scheibe kann aus einer Vielzahl von Orten kommen:
Object obj = new Object();
string delegate() dg = &obj.toString;
an diesem Punkt dg.ptr
Punkte obj
, die zufällig ein Garbage Collected Class Object ist, aber nur, weil ich es oben beschrieben habe new
.
struct MyStruct {
string doSomething() { return "hi"; }
}
MyStruct obj;
string delegate() dg = &obj.doSomething;
In diesem Fall obj
Leben auf dem Stapel durch, wie ich es oben zugeordnet, so auch die dg.ptr
zu diesem temporären Objekt zeigt.
Ob etwas ein Delegierter ist oder nicht, sagt nichts über das verwendete Speicherzuweisungsschema aus - das ist wohl gefährlich, weil ein übergebener Delegat auf ein temporäres Objekt verweisen könnte, das verschwindet, bevor Sie damit fertig sind! (Das ist der Hauptgrund, warum GC übrigens benutzt wird, um solche Bugs zu vermeiden.)
Also, wenn Delegierte von irgendeinem Objekt kommen können, warum wird angenommen, dass sie so viel GC sind? Nun, der automatisch generierte Abschluss kann lokale Variablen in ein GC-Segment kopieren, wenn der Compiler der Meinung ist, dass die Lebensdauer des Delegaten länger ist als die äußere Funktion.
void some_function(void delegate() dg);
void foo() {
int a;
void nested() {
a++;
}
some_function(&nested);
}
Hier wird der Compiler die Variable a
auf ein GC-Segment kopieren, da es some_function
davon ausgeht, eine Kopie davon halten und will use-after-free Bugs verhindern (die Schmerzen sind, wie es zu debuggen häufig führt zu Speicherbeschädigung!) sowie Speicherlecks.
Wenn Sie jedoch den Compiler versprechen, dass Sie es sich richtig machen werde durch die scope
Schlüsselwort auf der Delegierten Definition verwenden, wird es Sie vertrauen, und die Einheimischen verlassen genau dort, wo sie sind:
void some_function(scope void delegate() dg);
Keeping der Rest das gleiche, es wird keine Kopie mehr zuordnen. Auf der Seite der Funktionsdefinition ist es am besten, weil Sie als Autor der Funktion sicherstellen können, dass Sie keine Kopie behalten. obwohl
Auf der Verwendungsseite, können Sie es auch Umfang beschriften:
void foo() {
int a;
void nested() {
a++;
}
// this shouldn't allocate either
scope void delegate() dg = &nested;
some_function(&dg);
}
Also, der einzige Mal, Speicher automatisch vom GC zugeordnet wird, wenn lokale Variablen durch eine verschachtelte Funktion verwendet werden, die seine Adresse hat genommen ohne das Schlüsselwort scope
.
Beachten Sie, dass die () => whatever
und () { return foo; }
Syntax nur Abkürzungen für eine benannte verschachtelte Funktion sind, deren Adresse automatisch vergeben wird, so dass sie genauso funktionieren wie oben. dg = {a++;};
ist dasselbe wie dg = &nested;
oben.
Daher ist der entscheidende Vorteil für Sie, dass wenn Sie einen Delegaten manuell zuweisen möchten, Sie nur ein Objekt manuell zuweisen müssen und einen Delegaten von einer seiner Methoden machen, anstatt Variablen automatisch zu erfassen! Aber, Sie müssen die Lebensdauer überwachen und sie richtig freigeben. Das ist der schwierige Teil.
So für Ihr Beispiel:
auto del =() {
let res = f();
cell.write(res);
};
Sie könnten übersetzen, dass in:
struct Helper {
T res;
void del() {
cell.write(res);
}
}
Helper* helper = malloc(Helper.sizeof);
helper.res = res; // copy the local explicitly
send(&helper.del);
Dann wird auf der Empfangsseite, nicht vergessen free(dg.ptr);
, wenn Sie, so dass Sie don fertig sind Leck es nicht.
Oder, noch besser, wenn Sie send
ändern können, um nur Helper
Objekte zu nehmen, müssen Sie es überhaupt nicht zuweisen, Sie können es nur nach Wert übergeben.
Es kommt auch zu mir, dass Sie einige anderen Daten in diesem Zeiger packen könnten andere Daten an Ort und Stelle passieren, aber das abi Hacking und möglicherweise undefiniertes Verhalten würde. Versuchen Sie es, wenn Sie spielen möchten :)
Große Erklärung, danke. –