Pull to refresh

Comments 19

не поскажете, как заставить valgrind деманглить имена D?
В gdb почти полная поддержка D mangling, нужно только использовать свежую версию, с патчами от Iain Buclaw.
Прошу прощения! Вы правы, demangling в gdb действительно есть, но он не полный. Для проверки взял старый проект, чуть-чуть поправил, чтобы он упал в нужном месте и вот такой стек получил:
#0  0x000000000084b20f in draw.TestScene.prepareAbstractModel() (this=0x7ffff45cc300) at src/draw.d:56
#1  0x000000000084b06c in draw.TestScene.this(des.space.camera.Camera) (this=0x7ffff45cc300, cam=0x7ffff45cc158) at src/draw.d:39
#2  0x000000000080aa85 in _D3des4util4arch3emm21ExternalMemoryManager57__T6newEMMTC4draw9TestSceneTC6camera18MouseControlCameraZ6newEMMMFC6camera18MouseControlCameraZC4draw9TestScene (
    this=0x7ffff45cc238, _param_0=0x7ffff45cc100) at descore/import/des/util/arch/emm.d:49
#3  0x000000000084fa8c in window.MainWindow.prepare() (this=0x7ffff45cc200) at src/window.d:26
#4  0x000000000069ea60 in des.app.base.DesApp.addWindow(des.app.base.DesWindow() delegate) (this=0x7ffff45cd780, winFunc=...) at des/import/des/app/base.d:318
#5  0x000000000084f808 in D main () at src/main.d:11

Не деманглится имя шаблонной функции (фрейм 2), в остальном всё, кажется, в порядке. Возможно из-за того, что очень часто пользуюсь шаблонами и не заметил, что в остальных случаях всё работает.
Ошибки в demangling можно репортить через github.com/ibuclaw/gdb (стоит предварительно проверить на мастер версии)
Не стоит указывать аттрибуты шаблонным функциям, если они будут вызываться из @nogc кода, при этом внутри них будут вызываться тоже функции, помеченные как @nogc, компилятор инстацирует их соответствующим образом. Это поможет в случае повторного использования кода, когда внутри шаблонной функции будет вызываться не @nogc функция.

Что-то тут явно противоречие. Почему не стоит указывать атрибуты, если дальше как раз аргументация в пользу того, что стоит? Или я не понимаю, о чем здесь говорится.
наверное имеется ввиду что не стоит указывать @nogc в шаблоне, т.к. это перекроет инстанцирование шаблона для не- @nogc функций.
Для шаблонных функций не стоит указывать, чтобы сделать их более гибкими в использовании, необходимые атрибуты могут вычислиться компилятором в зависимости от контекта вызова (инстанцирования) функции. Для обычных функций нужно указывать.
Пример
import std.stdio;
import std.typetuple;
import std.range;

template isIRWL(R) { enum isIRWL = isInputRange!R && hasLength!R; }
template FloatHandler(R) { enum FloatHandler = is( ElementType!R == float ); }

float avg(R1,R2)( R1 a, R2 b ) // не указываем какая функция
    if( allSatisfy!(isIRWL,R1,R2) && allSatisfy!(FloatHandler,R1,R2) )
{
    auto c = chain( a, b );
    float res = 0.0f;
    foreach( val; c ) res += val;
    return res / c.length;
}

struct Bar
{
    float val;
    size_t cnt;

@nogc: // все функции в этой структуре такие
    this( float n, size_t c ) { val = n; cnt = c; }
    void popFront() { cnt--; }
    float front() { return val; }
    size_t length() @property { return cnt; }
    bool empty() { return cnt == 0; }
}

float call_nogc() @nogc // указываем конкретно
{
    // всё в порядке, avg инстанцируется как @nogc,
    // так как все вызовы в Bar @nogc
    return avg( Bar(1.0f,3), Bar(4.0f,6) );
}

struct Foo
{
    float val;
    size_t cnt;

    // здесь вызовы обычные
    this( float n, size_t c ) { val = n; cnt = c; }
    void popFront() { cnt--; }
    float front() { return val; }
    size_t length() @property { return cnt; }
    bool empty() { return cnt == 0; }
}

// код с ошибочным вызовом
//float foo_call_nogc() @nogc // функция помечена как @nogc
//{
//    return avg( Foo(1,10), Foo(8,2) );
//
//    пытается вызвать non-@nogc функции в Bar
//    attrib.d(54): Error: @nogc function 'attrib.foo_call_nogc' cannot call non-@nogc function 'attrib.Foo.this'
//    attrib.d(54): Error: @nogc function 'attrib.foo_call_nogc' cannot call non-@nogc function 'attrib.Foo.this'
//    avg инстанцируется соответствующим образом, как non-@nogc
//    attrib.d(54): Error: @nogc function 'attrib.foo_call_nogc' cannot call non-@nogc function 'attrib.avg!(Foo, Foo).avg'
//}

float foo_call()
{
    return avg( Foo(1,10), Foo(8,2) ); // тут всё в порядке, мы повторно используем avg
}

void main()
{
    writeln( call_nogc() );
    writeln( foo_call() );
}

Теперь все ясно. Стоит переформулировать в тексте, что не нужно ставить @nogc шаблонам — компилятор сам вычислит его необходимость: если все вызываемые из шаблонной функции @nogc и вызывающая @nogc (а может, это требование не нужно?), то и шаблонная будет @nogc. А если хотя бы одно из правил не выполняется, то шаблонная не будет @nogc.
еще вопрос из зала «Он же управляет выделением памяти. Его используют реализации таких встроенных типов как ассоциативные и динамические массивы, строки (что тоже массивы), исключения, делегаты.». как вернуть системе память выделенную под класс или массивы без GC — понятно. Непонятно про «делегаты». Для них выделяется память под контекст? И как эту память можно вернуть системе в случае если GC отключен?
Делегаты выделяют память, если требуется замыкание — перенос значения со стека, если оно используется возвращаемый из функции делегатом. Без GC замыканий лучше избегать.
Cпасибо!

вскрылся еще один неочевидный, для меня по крайней мере, нюанс: destroy() только вызывает деструктор, не освобождая память, занятую обьектом. Это означает, что при отключенном GC, например через "--DRT-gcopt=disabe:1", приходится полностью взять на себя управление памятью.

Так-же это означает, что если автор какой-либо библиотеки не озаботился управлением памятью в своём коде, то библиотека не сможет работать при отключенном GC. Это не фатально, просто полезно это явно осознавать.
Да, destroy определён таким образом специально, чтобы можно было явно вызывать деструкторы GC-объектов, не компрометируя при этом безопасность автоматического управления памятью.

Считается, что библиотеки вообще не должны выделять память каким-либо образом, оставляя это решения пользователю билиотеки, но, конечно, мало кто строго придерживается этого правила.
Насчёт выделения памяти библиотеками. Насколько я понял, сейчас подготовлены аллокаторы для предоставления выбора способа выделения памяти? Или логический смысл этой библиотеки другой?
Это API и реализация базового набора аллокаторов + инструментов для их композиции. Предназначены они не столько для настройки выделения памяти в библиотеках (как, например, STL allocators), сколько для реализации эффективных стратегий работы с памятью в пользовательских приложения.

Библиотеки (в идеале) должны вместо этого использовать range-based API и ленивые вычисления, чтобы откладывать принятие решений о способе аллокации как можно дольше.

Рассматривается вариант настраиваемых глобальных аллокаторов для new / delete, но я практически уверен, что это никогда не будет работать надёжно «из коробки».
Я мог-бы перевести одну или несколько статей по D, если в этом есть интерес. Если есть ссылки — присылайте, может что-то выйдет.
Может лучше всё же не переводить, а адаптировать интересную информацию под пользователей хабра?
Sign up to leave a comment.

Articles