Теперь о плохом.
Сначала то, что относится к языку как таковому:
1) Отсутствие перегрузки функций. Это большое зло. Это надо, это сокращает время для написания кода. Плюс этот недостаток порождает целый ряд других.
Пример:
Нужен менеджер ресурсов. Там нужен такой метод LoadRes(target:TypeOfResource var, url$). Вот при перегрузке функций, нужная функция в зависимости от типа нужного ресурса будет подставляться на этапе сборки. А без перегрузки я должен: первое - писать свой обработчик по типу ресурса (если сделать одну функцию с параметром типа общего объекта - Object) - тратить свое время на лишний код! И второе - этот код будет выполняться во время работы процесса, что будет снижать быстродействие! Уже два ОГРОМЕННЫХ МИНУСА!
Можно обойти это немного, конкретизировав методы загрузки ( LoadImage(url$), LoadSound(url$) ), но это немного снизит удобство. Т.к. для каждого ресурса надо будет помнить свою функцию.
2) Отсутствие конструкторов с параметрами. Писать функции для создания классов - не очень то и удобно с точки зрения поддержки и развития кода. - Их ведь нельзя наследовать и п1 - нельзя перегружать. Делать для инициализации статические методы (тоже что и функции по сути) при наличии потомков - тоже неудачно и упирается в отсутствие перегрузки функций и методов - см.сл.п.
3) Неправильная или глючная поддержка в потомках возвращаемых значений методов. Т.е. в потомке нельзя переопределить тип возвращаемого значения. Оно логично со второго раза, а с третьего - видно, что не менее логично, если тип возвращаемого значения - объект, то в методе потомка можно возвращать потомка от исходного значения-объекта. Чем плохо конкретно - тем, что нельзя писать одноименные методы-конструкторы для потомков и предков красиво и без извратов (приведения типов, что делает код громоздким и невыразительным). А так же методы копирования. Т.е., по сути, нет перегрузки методов (и функций), что неудобно.
4) Невозможность создания указателей на типы пользователя. Например я сделал тип TRGBA_Color с полями R, G, В, и А соответсвенно для цветов и прозрачности. Сделать так я не могу:
Local p:TRGBA_Color Ptr = TRGBA_Color Ptr(bmp.PixelPtr(0,0))
Т.е. получить указатель на пикселы и работать с ним красиво - p.R = 100. Приходится делать это указателем на байты, что с ЯП высокого уровня не очень то и ассоциируется. Похоже, это связано с тем, что каждый тип является объектом и переменная этого типа уже и есть указатель на объект и заботой о защите памяти (только safe - функционал). Приведение типов или индексный доступ, конечно, не защищены от "дурака" - прогламмера (unsafe - функционал), но позволяют "не дураку" нормально и удобно работать.
5) Нет команды SinCos, которая поддерживается новыми процессорами и позволяет быстрее производить операции просчета вращения и т.п., т.к. обычно надо и синус и косинус угла, а одной командой это явно сделать быстрее, чем двумя.
6) Статический step в циклах. Иногда надо, чтобы при проходе по массиву приращение индекса менялось. Приходится делать через while/wend.
7) Нет оператора With. Можно сделать аналог, но это увеличивает громоздкость кода.
8) Система слежения за освободившейся памятью (менеджер памяти языка) и автоматическим удалением не нужных уже объектов, хоть и хороша и удобна, но не совершенна. И порой возникали утечки из-за перекрестных ссылок (кросслинков). Т.е. все равно приходится следить за корректной очисткой памяти, что отвлекает от основного процесса написания кода, увеличивает объем текста программы и в сумме эти два фактора приводят к увеличению времени написания программы. Плюс это не характерно для ЯП высокого уровня - менеджер памяти должен быть на высоте. Так что это получается что-то среднее между ЯП высокого и среднего уровней.
Утечки возникают даже не при кросс-линках а и в ситуациях, когда возникать не должны. Например: объект-контейнер, содержит в себе список других объектов. Объекты содержат поле с линком на элемент списка типа TLink, - не на сам контейнер. На этот контейнер ссылается глобальная переменная, поэтому он и "живет". Глюк: если в методе объекта из списка контейнера глобальная переменная переопределяется (в "пусто" или другой контейнер), то память не очищается! Контейнер не удаляется! При освобождении объекта в другом месте - все нормально и память освобождается. Глюк лечится, если объект из контейнера при переопределении глобальной переменной выбрасывается из списка - Tlink.Remove(). Но вообще-то такого быть не должно.
9) Нет указателей на методы типов. Указатели на функции - есть, а на методы - нет. А это облегчило бы написание кода в некоторых случаях.
10) Нет нормальной полноценной совместимости (приведения) типов. Неоднократно сталкивался с тем, что надо было массив TObject[] преобразовать к моему типу (например для возврата из функции, для конвертирования из TList к массиву). Приходилось писать свои функции для преобразования вроде ObjArrayToTMytypeArray: TMytype[]( Object[] ). Можно в принципе и без этого - приводить типы уже во время работы с массивом, но наглядность и легкость чтения кода теряются. А это надо для реюзабельности кода.
П.с.: Таки сделали приведение типов для массивов - хорошо хоть развивается язык, а не стоит на месте.
П.п.с.: Млин. Криво работает. Нулевые массивы на выходе часто.
11) Нет директивы inline. Нужна она! И не только для ускорения работы кода.
12) Глюк с продолжением строки: если второй строкой идет комментарий, то выдается ошибка:
Field pms:TPixmap[] = [ ..
'LoadImage("images/gem_1_rotate.png") ..
- это нужно для комментирования при экспериментах с различными значениями в массиве.
13) Оптимизация кода очень плохая. Нет оптимизации по использованию методов - методы, которые ни разу не использовались в коде, все равно компилируются. Тот код, который пишется, буквально переводится один в один.
14) Нет инкапсуляции, т.е. защищенных и открытых методов и полей класса. Есть ее какое-то подобие на уровне модуля, но не в классе. А отсутствие инкапсуляции - это есть unsafe.
15) Нет автоматического приведения типов. Т.е. если в функции объявлен один тип, а передается другой, причем совместимый (общий класс Object), возникает ошибка.
16) Игры с типами не доделаны. Вот начал я привыкать к этим играм с типизацией и ощутил необходимость ссылки на тип. То, что в С++ шаблонами можно сделать. Такая переменная в которой нужный тип обозначен - очень нужно для наследования и для различных фабрик объектов, чтоб кода писать меньше. А то можно и приустать конструкторы под каждый новый тип переписывать, раз уж нет их с параметрами и перегрузки функций тоже нет.
П.с.: !!! Я придумал, как это обойти. В нужных нам классах метод Copy(), создающий копию класса. Неужели сделали перегрузку хоть на уровне преобразования возвращаемого результата с учетом наследования. Но - нужно самому писать весь код по копированию.
17) Нет автоматических конструкторов копирования объектов. Приходится писать самому - а это время. А время - деньги.
18) Несколько глупо реализовано конструирование объектов при наследовании. Переопределенный конструктор предка вызывается по любому, даже если мы его не вызываем и его вызов нам не нужен, т.к. мы переопределяем конструктор в наследнике. Практический пример: нужно НЕЗНАЧИТЕЛЬНОЕ изменение предка. Предок использует поле объекта одного типа, в наследнике нужно это же поле, но другого типа. В конструкторе этот объект создается. В наследнике значит надо переопределить создание этого объекта на нужный нам тип. Но в конструкторе предка этот объект тоже создается, а потом пересоздается в наследнике. Конечно, менеджер памяти лишний объект потом удалит, но вообще получается очень глупое и неразумное использование ресурсов. Хорошо если этот объект маленький, а если требует серьезных расчетов, подгрузки ресурсов и т.д. - получится заметное снижение производительности. Поэтому предок надо писать заново либо с нуля, либо с предка предка, еще не имеющего нужных нам полей объектов. Вывод: Язык не очень дружелюбен к наследованию и, следовательно, к хорошей поддержке крупных и долгосрочных проектов.
Теперь то, что можно отнести к недостаткам библиотек:
19) Нет списка со значениями, не являющимися объектами. Например, целыми - Int.
20) Списки не очень удобно реализованы. Было бы хорошо наличие у элементов списка TLink поля Parent, для ссылки на родительский список (И поля Child для многоуровневых древовидных списков). Это связано с менеджером памяти языка, который не в состоянии корректно удалять объекты с кросслинками.
21) Утечка памяти в функции LoadImage при загрузке *.png. Ее практически не заметно, т.к. вылазит она при особых случаях, например при перегрузке картинок, что обычно не требуется - они загружаются один раз обычно. Но, например, при отсутствии менеджера ресурсов, когда нам нужно перезагружать изображение (при обработке скриптов, например), то утечка становится заметной. Причем внутренний меденжер памяти утечку не показывает.
Прим.: при загрузке *.jpg утечки нет.
22) При создании нового pixmap, он не инициализируется. Т.е. мы создали новый pixmap, а там мешанина из пикселов, а не черный или белый или заданный программистом фон. Надо писать функции заполнения новообразованных пиксмапов, а это - время/деньги.
23) Неясности со шрифтами: LoadImageFont("Arial.ttf"…) не приводит к ожидаемому результату, а с полным путем LoadImageFont("C:\Windows\Fonts\Arial.ttf"…) все работает. Хотелось бы без указания путей. Хоть и есть фонт по умолчанию.
24) Отсутствие такого хорошего понятия как контекст устройства. Есть в вынь и в пурике ( StartDrawing(OutputID) ). На CreateGraphics(…) его заменить нельзя - если разрешение не поддерживается, контекст не создается. Это очень-очень плохо. Нельзя рисовать нормально в оффскрин окно. Другими словами рисовать в текстуру (спрайт) можно только ручками через массив пикселей, что медленно. Сторонние библиотеки для этого - платные.
25) Метод pixmap. Paste(…) не проверяет размеры - это приводит к глюкам с затиранием памяти и необходимостью писать много чего своими ручками. Это unsafe (при том, что полезные вещи в страхе этого самого unsafe не делаются), и время/деньги на дополнительный код.
26) Глючность идиотическая, надеялся, что такого не встречу. Пример: когда работа с пиксмапами формата PF_RGBA8888 проходит отлично, а смена формата на PF_BGRA8888 приводит к непонятным глюкам в официальных библиотечных функциях.
27) Глюки при работе с созданием пиксмапов, если их делаешь много, то возникает глюк. Этот глюк как-то связан с форматом картинки. При формате PF_BGRA8888 он возникает раньше. GCCollect() не помогает, дело не в переполнении, а хз в чем.
Теперь то, что больше относится к написанию игр собственно:
28) Нет поддержки шрифтов с произвольной шириной и утилит для работы с ними. Моноширинные - это вчерашний день.
29) Нет зачатков даже движка как в Blitz3D. Есть просто набор либ для работы с сетью, звуком графикой и устройствами ввода (мышь, клавиатура, джойстик). Джентльменский набор, но "ни цента больше".
30) Нет и намека на партиклы. О редакторе молчу, хотя бы классы или либы для работы с ними были какие-то. А современный уровень игростроения их требует. Все надо делать самому - опять время/деньги. Хотя есть пара сторонних убогих движков частиц. И даже с редакторами.
31) ГУИ. Игровое конечно. Опять полное отсутствие. Хотя тут проще - есть сторонние библиотеки. И даже бесплатные.
32) Нет поддержки треккерных форматов звука. Но можно использовать сторонние библиотеки.
33) Нет физики. Есть сторонние модули.
А в дальнейшем, по мере моего желания, будет рассказываться о путях обхода описанных препятствий и просто полезных вещах при программировании на BlitzMax. Я планирую делать по посту в неделю, а как получится - увидим.
Подписаться на:
Комментарии к сообщению (Atom)
3 комментария:
В результате обсуждения на форуме, благодаря Сержу Петровскому, была внесена поправка в п.33 - есть сторонние модули для физики.
Пункты 8 и 9 перенесены на 19 20 - из косяков языка в недостатки библиотек. Тоже благодяря обсуждению на форуме и наблюдательности Амира.
Решил сделать разбивку по пунктам, т.к. на блоггере сильно долго орабатываются большие посты - так при правке этого приходится ждать минут по 5 (наверное там ЦРУ-шники в реальном времени делают вычитку текста на предмет провокаций и призывов к провокациям 8))
Да и систематизировано будет лучше.
Отправить комментарий