LINUX.ORG.RU

MSEide vs Lazarus


0

1

Здравствуйте, а вот и снова я!

За прошедшее время я перестал валять дурака и устроился на нормальную работу. Т.е., опять пишу на Delphi под офтопик, на этот раз под Firebird (который мне теперь уже почти нравится).

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

Как я спасся? Ну, во-первых, если это TComponent, то можно создавать его внутри другого TComponent, и рано или поздно объект всё же будет уничтожен. Иерархическая организация памяти с именами, на самом деле, не так уж сильно уступает лисповой куче по удобству (и мы вспоминаем tcl, на котором я, наверное, строчек 50 за всю жизнь написал).

Но это и так понятно. И это плохо подходит для временных объектов внутри функций. А такие объекты зачастую нужно поместить на стек. Object Pascal не особо дружественнен в этом отношении - объекты создаются на куче. Но, поскольку Object Pascal - это всё же язык довольно эклектичный (можно даже сказать - мультипарадигменный), то прагматично-приемлемый выход нашёлся. В итоге я пишу так:

function TMyClass.myMethod; 
var x:TSomeObject; 
var y:TSomeOtherObject;
var fr:stackFr; // магическое заклинание
begin 
fr:=mkStackFrame; // магическое заклинание

x:=TSomeObject.Create; 
onStack(x); // теперь x будет уничтожен 
            // при выходе из функции
y:=TSomeOtherObject.Create;
onStack(y); // y будет уничтожен при выходе раньше x. 
DoSomething;
AndSomethingMore; 
end.

По учебнику нужно было бы писать это так:

function TMyClass.myMethod; 
var x:TSomeObject; y:TSomeOtherObject;
begin 
x:=TSomeObject.Create; 
try 
  y:=TSomeOtherObject.Create;
  try 
    DoSomething;
    AndSomethingMore; 
  finally
    FreeAndNil(y);
    end;
finally
  FreeAndNil(x); 
  end;
end;

Уровень вложенности, таким образом, растёт с каждым временным объектом. Меня от этого раньше тошнило, а теперь я избавился от этого. Конечно, платой за это является некоторая потеря скорости, но всё равно, Питону нас никогда не догнать :-) Т.е., в прикладном коде этот метод вполне применим и я им уже пользуюсь довольно массированно. Также есть и ограничение - создаваемый объект должен быть потомком TObject, но это ограничение реализации, его легко преодолеть, перекрывая функцию onStack - главное, чтобы у объекта был деструктор или что-то в этом роде.

Как это сделано - смотрите в исходник: http://ecovillage.narod.ru/pascal/uStackUtil.pas Также там реализованы special variables Лиспа. Правда, я сделал special переменные только типа variant, но ведь в лиспе вообще обычно все переменные - типа variant, и ничего.

Если кто-то скажет мне, что это - велосипед и как сделать это более грамотно - скажу спасибо. Может быть, я просто отстал от развития Object Pascal и теперь это делается как-то попроще?

А при чём же здесь open source? Я проделал небольшой тест и, если кое-что выкинуть, то модуль собирается и во FreePascal (в режиме совместимости с Delphi) и даже, вроде, работает.

В общем, так получается, что мне придётся написать кое-что для себя на Delphi, но я подумываю о том, чтобы взять вместо этого FreePascal. Поэтому, чтобы два раза не вставать, собственно переходим к вопросу: пробовал ли кто-нибудь MSEide и насколько оно жизнеспособно? Lazarus вроде как-то туго идёт, или я ошибаюсь?

★★★★★

Последнее исправление: den73 (всего исправлений: 1)

Питону нас никогда не догнать :-)

по скорости разработки? :)

PS маладца, есть голова на плечах. Но дельфи мёртв, рекомендую паралельно учить что-нить более актуальное, а то потом будут сложности с работой.

true_admin ★★★★★
()

>сё же, Object Pascal - это, потенциально, правильный язык
Component pascal

yaws
()

>Всё же, Object Pascal - это, потенциально, правильный язык, что бы о нём не говорили.

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

anonymous
()

Если кто-то скажет мне, что это - велосипед и как сделать это более грамотно

Я не скажу что это велосипед.ю я скажу.ю что это изврат. Причем в смысле сопровождения изврат.

Более грамотно сваять сборщик, например а-ля boehm

http:// ... .pas

if s<>'' then msg:=copy(msg+#13#10+'Âûçîâû: '+s,1,512);

То что в fpc собирается, это хорошо, но:

1. \r\n в никсах принято \n (#13) - надо разруливать {$ifdef} и вообще, всю магию вынести в константы. А еще кодировочка... Ну хрен с ней

2. отступы в блоках в один пробел некошерны даже для дельфей

function mkStackFrame:ITObject;

begin result:=TStackFrame.Create as ITObject; end;

3. Прежде чем делать thread-safe, прикрути обработку ошибок памяти...

Lazarus вроде как-то туго идё

В последний раз, как я его использовал, мне реально только TWebBrowser не хватило :)

ps. код по совокупности нечитаем. отформатируй код с учетом пожеланий, выкинь закомментированные участки. Ведь именно изза такой от каши и сложилась тенденция считать паскалеводов быдлокодерами.

yaws
()
Ответ на: комментарий от yaws

не самый лушший, но все же пример форматирования кода:

	generic FMap<A, B> = class

	type public

		TSrcList = specialize WXList<A>;

		TSrcArray = array of A;

		TDstList = specialize WXList<B>;

		FX = function(x: A): B;

		FY = function(x: A; y: B): B;

	var public

		class function Map(f: FX; args: TSrcList): TDstList; overload;

		class function Map(f: FX; args: TSrcArray): TDstList; overload;



		class function Fold(f: FY; initial: B; args: TSrcList): B; overload;

		class function Fold(f: FY; initial: B; args: TSrcArray): B; overload;

	end;

yaws
()
Ответ на: комментарий от yaws

> В последний раз, как я его использовал, мне реально только TWebBrowser не хватило :)
Спасибо, это важно.

ps. код по совокупности нечитаем.

Я думаю, просто у нас разный стиль отступов. Отступы у меня вроде два пробела на уровень, если один - то это очепятка. Тело функции я не выделяю отступами, потому что они не несут информационной нагрузки. Я - не из тех, кто ищет заголовки функций, листая экраны.

В целом, данный код не является законченным для публикации. Он делался очень быстро и между делом. Я им пользуюсь (кстати, не всем, что там есть, про половину я сразу же забыл и реально пользуюсь только mkStackFrame и onStack). Думаю, его следовало бы запостить в какой-нибудь проект, только я не понял, в какой. Есть jcl/jvcl, но я не знаю, подходит ли он под FPC. Хотя вроде тоже опен-соурс. Вроде там встречается {$ifdef freepascal}, но неясно, насколько это всё реально. Если будет ясно, куда запостить, то я готов подправить стиль под стиль данной библиотеки и даже перевести комменты на англояз.

Я не скажу что это велосипед.ю я скажу.ю что это изврат. Причем в смысле сопровождения изврат.


Более грамотно сваять сборщик, например а-ля boehm.

Я не очень себе представляю, как это возможно. Для сборщика нужна функция - перечень ссылок данного объекта на другие объекты. И тут мы сразу сталкиваемся с трудностями.
1. Часть ссылок является private полями объекта, которые не доступны ни через RTTI, ни путём чтения исходников и создания хак-классов.
2. Даже если бы они и были доступны, нам нужно всё равно будет реализовать эту функцию для
3. Если только часть типов мы кладём в сборщик, а часть не кладём, то (неучитываемая сборщиком) ссылка от несобираемого объекта на собираемый становится граблями.
4. В component pascal есть деструкторы. Деструкторы несовместимы со сборкой мусора, т.к. они должны вызываться в определённом порядке по построению той же VCL. В моём же решении (которое, по сути, просто представляет из себя размещение объектов на стеке), такой порядок не только существует, но и легко вычитывается из текста.

Если есть какие-то практически приемлемые способы обойти названные трудности, прошу в студию (идеи, а не код). Хотя я готов поспорить на кило кило апельсинов, что хорошего способа не найдётся.

Кроме того, сборка мусора как раз тем и плоха, что она затрудняет контроль над гладкостью выполнения. Одна из остающихся ниш, в которых Паскаль, я считаю, силён - это ниша программ обрабтки данных. Сборка мусора в таких программах мешает (а иначе я бы писал все программы для себя на просто на лиспе и не думал бы ни о чём).

den73 ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> PS маладца, есть голова на плечах.
Спасибо :)

Но дельфи мёртв,

Мёртв-не мёртв, а зарплата на карточку приходит. Даже COBOL ещё не мёртв. Необязательно учить всё новое, нишевые программисты тоже могут жить неплохо. Кроме того, новое - не лучше старого, к сожалению. А там, глядишь, я и сам помру и мои расходы заметно поубавятся.

по скорости разработки? :)

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

den73 ★★★★★
() автор топика

А тем временем я подбираюсь к тому, чтобы сделать слабые хеш-таблицы по любому наследнику TObject. Общая идея такая: ключём хеш-таблицы будет адрес объекта.

Мы перекрываем менеджер памяти, чтобы он на любой запрос возвращал не сколько попросили, а на sizeOf(pointer) больше. При этом, лишний поинтер (назовём его extraData) забивается каким-то заданным значением. Далее, у объекта идём по адресу @self+classType.instanceSize (если правильно помню эти слова) и в них можно разместить указатель на некий record. Именно этот record, а не сам объект, будет заноситься в хеш-таблицы в качестве представителя объекта. Естественно, record должен содержать ссылку на сам объект.

Далее, существует два варианта реализации слабости, оба они неправильные. Вариант N1 - перекрываем деструкторы всех типов, которые мы хотим класть в хеш-таблицу. Это делается с помощью шаманства над VMT. Новый деструктор будет вызывать заданную функцию (назовём её objectIsDeleted) над extraData. Функция не уничтожает extraData, но ставит указатель на объект в nil, что и позволяет (лениво) исключить объект из всех хеш-таблиц. Как только функция getHash находит extraData с нулевым объектом - она удаляет его из хеш-таблицы.

Что тут неправильного? Мы не можем подменить менеджер памяти сразу при запуске программы. Пройдут инициализации некоторых модулей, будут выделены на куче некоторые объекты не увеличенного, а обычного размера. В этих случаях extraData будет указывать в туман. И не дай нам Бог вызвать функцию objectIsDeleted над этой extraData.

В jcl есть функция sizeOfMem или что-то в этом роде, но она у меня всегда возвращает ноль почему-то.

Второй способ аналогичен первому, только функция objectIsDeleted вызывается на freeMem. Опасность - ровно та же самая, что и в первом случае, плюс к тому, если был realloc, то extraData могла потеряться. Хотя это можно пытаться отловить внутри realloc нашего менеджера памяти.

В extraData можно хранить что угодно. Например, аналог лиспового property list, или любые дополнительные поля, привязываемые к конкретному экземпляру объекта. Доступ к этим полям будет быстр - через обращение по указателю. Т.е., получаем эффективный способ ассоциировать с любым объектом произвольные данные.

Такой менеджер памяти я сделал (без shareMem) и подключил к своей нехилой программе. Она вроде не падает, а работает, и даже закрывается успешно. Удивляться тут нечему, поскольку деструкторы я пока не поменял, т.е., грабли, на которые надо наступить, ещё не сделаны.

Жду критику (например, объяснение, почему я обязательно наступлю на грабли при подмене менеджера памяти) и предложения о том, как сделать extraData наиболее компактной и полезной.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

а зарплата на карточку приходит.

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

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.