четверг, сентября 10, 2009

Delphi 2010. RTTI.

У advanced records невозможно получить список свойств. FUCK :(((

10 комментариев:

Анонимный комментирует...

В делфях RTTI заточены на таблице VMT. Если нет VMT, то в рантайме ее неоткуда брать, кроме той что может вставить компилятор. По этой же причине не работают свойства в старых объектах, пока не добавишь хотя бы один виртуальный метод.

Kazantsev Alexey комментирует...

RTTI для записей тем не менее существует, поля то получить можно. Какого они не сделали этого для свойств...

Анонимный комментирует...

Неясно, зачем вообще нужны advanced records - чем TObject не устраивает?
Тип record хорошо вписывается в процедурное программирование, жаль, что там его и не оставили. Нет, ведь нужно было делать аналог struct в С++
:(

Kazantsev Alexey комментирует...

А как же перегрузка операторов? А хелперы? Чрезвычайно удобные штуки, от которых пришлось бы отказаться.

Анонимный комментирует...

> А как же перегрузка операторов?
А причем здесь records ? Их же для классов не реализовывали разве?
> А хелперы?
То же самое - к записям отношения не имеет.

Насчет полезности helpers у меня сомнения - слишком сильное нарушение инкапсуляции, так как мы можем "взломать" любой класс, не утруждая себя проектированием. Разработчики предупреждали:
"Class helpers provide a way to extend a class, but they should not be viewed as a design tool to be used when developing new code. They should be used solely for their intended purpose, which is language and platform RTL binding."
(http://edn.embarcadero.com/article/34324).

Вернемся к записям. В Delphi сейчас есть классы (class), есть записи (record) и есть объекты (object). И object, и record размещаются в статической памяти и не имеют общего предка. И в чем тогда разница? Object имеет статус "backward compatibility only", т.е. планировалось, что его полностью заменит class.
Advanced records - мало того что реинкарнация object, так еще и новая синтаксическая конструкция.

Для хранения данных вполне подходил class - накладные расходы 4 байта (правда, в последних версиях вроде 8), но зато общий предок и конструкторы/деструкторы.

Что же до издержек на создание экземпляра класса в динамической памяти, то можно попробовать
реализовать пул экземпляров, переопределив NewInstance у данного class type.
--
Eugene

Kazantsev Alexey комментирует...

>А причем здесь records ? Их же для классов не реализовывали разве?

Перегрузка операторов, в нативной Delphi (о призме я не говорю), реализована только для записей.

>> А хелперы?
>То же самое - к записям отношения не имеет.

Объекты (object) не могут иметь хелперов, записи могут.

>Насчет полезности helpers у меня сомнения - слишком сильное нарушение инкапсуляции, так как мы можем "взломать" любой класс, не утруждая себя проектированием.

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

>Вернемся к записям. В Delphi сейчас есть классы (class), есть записи (record) и есть объекты (object). И object, и record размещаются в статической памяти и не имеют общего предка. И в чем тогда разница?

Записи поддерживают перегрузку операторов, объекты (object) нет. Записи могут иметь хелперы, объекты (object) не могут.

>Advanced records - мало того что реинкарнация object, так еще и новая синтаксическая конструкция.

Скорее, просто расширенная ;)

>Для хранения данных вполне подходил class - накладные расходы 4 байта (правда, в последних версиях вроде 8), но зато общий предок и конструкторы/деструкторы.

Дело не только в хранении данных. Есть необходимость в интеропе с внешними API, тут классы не помогут (класс, суть черный ящик с публичным интерфейсом). А продвинутые структуры позволяют нам увеличить инкапсуляцию при работе с данными (это наглядно демонстрирует хэлпер для TRTLCriticalSection из SyncObjs)

>Что же до издержек на создание экземпляра класса в динамической памяти, то можно попробовать
реализовать пул экземпляров, переопределив NewInstance у данного class type.

На самом деле издержки не столь велики, FastMM прекрасно справляется. Я пробовал делать пул часто создаваемых объектов для своей реализации пула потоков. Затея оказалась напрасной. Пул объектов работал ни чуть не быстрее обычной аллокации.

Анонимный комментирует...

> Дело не только в хранении данных. Есть необходимость в интеропе с внешними API, тут классы не помогут (класс, суть черный ящик с публичным интерфейсом).

Класс - фактически та же запись, вернее указатель на нее. Впрочем, действительно, классы для взаимодействия с внешними библиотеками использовать не принято. Возможно, и зря. Если разработчики обеспечили бы гарантии выравнивания полей в классе a la record (не знаю, как сейчас), то можно было бы обойтись и классом.

> Перегрузка операторов, в нативной Delphi (о призме я не говорю), реализована только для записей.

Вы сравниваете record vs object, а я record vs class. Вашу позицию я понял. В любом случае, им стоило выбросить object из языка, но к сожалению, они будут тащить его и дальше, наряду с архаизмами вроде absolute.

Такое ощущение, что сложность языка и компилятора в последних версиях уже вышла из под контроля, и подобные грабли будут попадаться постоянно.
--
Eugene

Kazantsev Alexey комментирует...

>Вы сравниваете record vs object, а я record vs class. Вашу позицию я понял.

Перегрузка операторов это самое большое преимущество записей перед классами. Кроме того, отсутствие необходимости в динамическом размещении делает записи очень удобными для "мелкой инкапсуляции", что можно увидеть на примере TStopwatch, использовать который, будь он в виде класса, было бы чрезвычайно неудобно.

>В любом случае, им стоило выбросить object из языка, но к сожалению, они будут тащить его и дальше

Они как то делились планами по поводу переписывания компилятора с выкидыванием накопленного мусора. Возможно object и выкинут.

>наряду с архаизмами вроде absolute.

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

>Такое ощущение, что сложность языка и компилятора в последних версиях уже вышла из под контроля, и подобные грабли будут попадаться постоянно.

Согласен. Не зря они собирались компилятор переписать :) А тут еще x64 и кроссплатформа...

Анонимный комментирует...

> Перегрузка операторов это самое большое преимущество записей перед классами.
Стоп, смотрю http://edn.embarcadero.com/article/34324, раздел Operator Overloading - как раз для классов сделано. Это не работает?

> Какой же это архаизм?
Для языка высокого уровня - несомненный архаизм, наряду с goto.
Подобное приведение типов, думаю, лучше реализовывать через записи с вариантами. С absolute можно легко залезть в чужую память, особенно при изменении размеров типов.

Пример использования в справке Delphi

var
Str: string[32];
StrLen: Byte absolute Str;

по моему мнению - аккуратно разложенные грабли. Premature optimization в чистом виде.
--
Eugene

Kazantsev Alexey комментирует...

>Стоп, смотрю http://edn.embarcadero.com/article/34324, раздел Operator Overloading - как раз для классов сделано. Это не работает?

Работает только для .NET-версий Delphi. Нативная Delphi этого не поддерживает.

>Для языка высокого уровня - несомненный архаизм, наряду с goto.

Будучи высокоуровневым языком, Delphi позволяет использовать любые низкоуровневые трюки, что выгодно отличает его от современного мейнстрима. Авторы языка это понимают, и не только не отрекаются от низкоуровневости, но еще и делают ее более удобной ($POINTERMATH, как пример)

>Подобное приведение типов, думаю, лучше реализовывать через записи с вариантами.

Это не избавит от необходимости делать приведение типов: TMyVarRec(Value).NativeInt; TMyVarRec(Value).Pointer;

>С absolute можно легко залезть в чужую память, особенно при изменении размеров типов.

Главное не забывать стелить соломку:
Class Operator XmlRpcBinary.Implicit(Const AValue : Variant) : XmlRpcBinary;

{$REGION ' Контроль размера типа '}

{$IF SizeOf(XmlRpcValue) <> SizeOf(Variant)}

{$MESSAGE FATAL 'Absolute. SizeOf(XmlRpcValue) <> SizeOf(Variant)'}

{$IFEND}

{$ENDREGION}

Var

Value : XmlRpcValue Absolute AValue;

>Пример использования в справке Delphi

var
Str: string[32];
StrLen: Byte absolute Str;

по моему мнению - аккуратно разложенные грабли. Premature optimization в чистом виде.

Этому примеру сто лет в обед :) Правда, сейчас так давно никто не делает, в этом нет нужды.

Я приведу еще пример кода, когда absolute помогает облегчить, и написание кода, и его чтение:
Function EncodeBase64(Const AData; ADataSize : Integer; Const ABuffer; ABufferSize : Integer) : Integer;

Var

Data : PAnsiChar Absolute AData;
Buffer : PAnsiChar Absolute ABuffer;

Внутри функции работать удобнее именно с PAnsiChar т.к. появляется возможность адресоваться к чарам, как к элементам массива + адресная арифметика.