Как мне выделить делегата в куче? - PullRequest
0 голосов
/ 06 ноября 2019

Как мне куча выделить делегата и получить указатель на него?

Я хочу иметь возможность привести его к пустому указателю и обратно, чтобы сохранить его в гетерогенном массиве.

1 Ответ

3 голосов
/ 06 ноября 2019

a delegate в D, подобно динамическому массиву, представляет собой простую структуру с 2 полями указателя. Он имеет поля:

  • .ptr, который является указателем контекста (this ссылка, замыкание, указатель фрейма стека)
  • .funcptr, который является указателем функции

Итак, если вы абсолютно уверены, что указатель контекста не будет уничтожен при вызове сериализованного делегата (лучше всего для ссылки на класс this, то есть чаще всего для функции-члена класса),Вы можете просто сохранить структуру в куче с этими 2 void* значениями или сделать копию ее байтов, а затем десериализовать ее обратно делегату.

Пример:

alias MyDelegate = void delegate();

void main()
{
    int local;
    MyDelegate dlg = () { import std.stdio; writeln("delegate ", local); };

    // T.sizeof always gives the size of what the size is as a struct.
    // So not the size of what something points to (e.g. class is size_t.sizeof)
    // Copies the bytes on the stack stored for the delegate, doesn't pin the
    // context pointer or anything like that which would make it a safer operation
    auto heap = (cast(ubyte*)&dlg)[0 .. MyDelegate.sizeof].dup.ptr;

    local = 5;

    callHeapDlg(heap);
}

void callHeapDlg(ubyte* heap)
{
    // This could be a potentially unsafe cast if casting to some other delegate!
    // You might be casting away safety attributes or other things. Best to
    // restrict the type of the delegate you convert to heap from the start.
    // Simply recreates the delegate by casting a pointer to the bytes you have
    // copied to a pointer to a delegate object and then dereferencing it.
    // This copies the pointers from the heap back to the stack here.
    auto dlg = *(cast(MyDelegate*) heap[0 .. MyDelegate.sizeof]);

    dlg(); // prints "delegate 5"
}

Но никогда не забывайте, что это небезопасная операция, из-за которой вы могли бы потенциально повредить ваше приложение и даже случайно ввести выполнение произвольного кода. Особенно, если ваш указатель контекста делегата выходит из области видимости и освобождается, вы можете столкнуться с серьезными неразрешимыми проблемами.

В этом случае, например, не следует вызывать делегат, выделенный для кучи, вне основной функции, поскольку он, скорее всего, содержитуказатель на текущий кадр стека главной функции, который становится недействительным после его выхода.

См. https://dlang.org/spec/abi.html#delegates

...