LINUX.ORG.RU

Муки выбора языка программирования

 , , , ,


2

4

Пытаюсь выбрать язык программирования для личного проекта.

Хочется, чтобы у языка были:

  • библиотека для загрузки/выгрузки изображений с поддержкой широкого круга форматов
  • биндинги для sdl2
  • работа с битовыми массивами размером больше чем 64 элемента (с поиском единиц)
  • перегрузка оператора индекса в том числе при присвоении
  • ассоциативные массивы с лаконичным доступом к элементам
  • документацией с поддержкой мобильного просмотра в 2023 году-то
  • поддержкой компиляции для мобильных архитектур
  • нормальный полиморфизм, а не как в Rust
  • востребованность на рынке труда

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

C++ и Rust имеют очень странные конструкторы для битовых массивов. Может это проблема документации, но я с ходу не нашёл как мне создать битовый массив из готового байтового массива, чтобы каждый байт превратился в 8 бит.

Haskell имеет поддержку даже многомерных битовых массивов, но вот документацию на мобильном листать не удобно. В принципе не критично, но я не уверен что haskell вообще подходящий инструмент для моей задачи. А задачу мою можно найти по тегу «гексагональный пиксель» здесь.

Что выбрать?

★★★★★

Ответ на: комментарий от annulen

Если для решения поставленной задачи достаточно производительности с -O0, значит ЯП был выбран неверно.

программа НИКОГДА не решает поставленной задачи… хотя бы потому, что ставить строго задачи никто не умеет, и задача мутирует по мере ее решения. проще говоря, это погоня ослика за морковкой.

отсюда - чем язык эффективней, тем лучше.

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

они там совсем, чего они творят?

Здесь всё как раз проще простого. Компилятор может оптимизировать, предпологая, что UB быть не может. У тебя в функции fermat() цикл, из которого можно выйти только по return true, а иначе это бесконечный цикл. Бесконечный цикл - это UB, его не может быть по определению. Значит возможен только второй вариант: return true. Вывод: функция fermat() всегда возвращает true.

Проверяем: https://gcc.godbolt.org/z/1cEKKcKs5

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

Да, верно. Мне почему-то привиделось, что из fermat() мы вышли по return false. Хоть я и не в восторге от такого отношения к вечным циклам, но все же это не такая уж жесть как я подумал сначала. Верное замечание.

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

Предлагаете сделать санитара неотъемлемой частью программ на Си++? Запустите тест производительности с включенным санитаорм.

В 20 раз говорю - в тестах санитары использовать. И если тестируемая единица полезла в чужую память, то это станет видно. Точно также надо поступать и безопасном Расте, а не читать его unsafe вдесятером по три раза. И вобще, будто бы контеск не нужен, чтобы распарсить небольшой кусок, нужно проштудировать всю портянку перед.

Что значит «сломали»? В документации к питону 2 где-то написано, что если написан код «…», то может произойти что угодно?

Ну получается так, код на Питон 2 превратился в тыкву, (почти как не прошел тесты в новой версии компиля). Классический признак ЮБ. Если бы в доках написали, что это ЮБ, то можно было бы не признавать отказ от обратной совместимости, а сказать, что нефиг так было писать. Например, не надо было считать, что 5/2 является целочисленным делением, а следующей мажорной версии начнут вообще возвращать остаток от деление, все норм, правил то нет.

Я же привёл пример. Есть какой-нибудь цикл:

Ценность такой оптимизации == нулю, таких циклов очень мало, они пишутся либо по ошибке, либо специально в embedded. Бесполезным и вредным занятием является борьба с вечными циклами.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

Ценность такой оптимизации == нулю

Не могу не согласиться.

Лучше просто влепили бы abort()

Вообще говоря - лично я предпочёл бы увидеть ошибку компиляции.

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

Onescript. Компилируется при помощи Mono. Ничего платного.

В Visual Studio 2013 он не собирается, так как проекты подготовлены для Visual Studio2015.

Но лицензия Visual Studio 2013 позволяет ее бесплатно использовать не более пяти программистам (у нас лишь я ее использую).

Остальные Visual Studio не имеют таких лицензий, да еще вдобавок для них нужна Windows 10, а у нас ни на одном компьютере ее нет (да и в целом ОС и железо старое).

Кроме того Onescript использует .Net, а для него опять таки подавай Windows 10.
Точнее версия Visual Studio должна быть больше 2013, а у них нет бесплатной лицезии, да еще Windows 10 нужен.

Не сростается.

Для нашего железа подойдет: C++, OpenGL, Lazarus, Python (с версией не выше 3.7), ... (все должно быть бесплатным).

Кстати имеют многолетний опыт разработки для 1С 7.7 и 1C 8.
Начальник сказал - «Больше мы разработку на 1С не будем вести».
Предприятие не хочет приобретать лицензии (даже с заправкой картриджей постоянные финансовые проблемы).

Хорошо то, что с зарплатой все ok.

Честно говоря у меня уже по существу разработана своя 1С (даже для Moxcel свой API на 100% совместимый с 1C 7.7 + плюшки, которых в 1С 7.7 нет).
Можно работать с любой конфигурацией 1С 7.7 без dll фирмы 1С.
Кстати архитектура разрабатываемого API для использования метаданных позволяет без проблем конвертировать конфигурации 1С 8 в свой формат метаданных.
Впрочем API для работы с метаданными позволяет пригодно для работы с 2D, 3D, ... (конфигурации 1С использовались лишь для отладки API).
И еще API ни на 0.000000000001% не «прибито гвоздями» к 1С.

GUI нужен свой.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 7)
Ответ на: комментарий от kvpfs

В 20 раз говорю - в тестах санитары использовать.

Выше по этой ветке человек хотел такого: «произвольное поведение» должно быть ограничено как минимум правилом нанесения минимального ушерба, или критерием неразрушающего поведения.

А так всё нормально, в тестах санитары, в рабочей версии неограниченное неопределённое поведение.

Ну получается так, код на Питон 2 превратился в тыкву, (почти как не прошел тесты в новой версии компиля). Классический признак ЮБ.

Нет. Признак ЮБ, если этот код начал делать что угодно. А он просто в новой версии компилятора не компилируется или выбрасывает исключение. Это нормально.

Ценность такой оптимизации == нулю, таких циклов очень мало, они пишутся либо по ошибке, либо специально в embedded. Бесполезным и вредным занятием является борьба с вечными циклами.

Не с вечными, а с теми, результаты которых не используются.

Например, в развёртке какого-нибудь шаблона может быть что-то вроде:

{
  process_data(...);
  int check = 0;
  for (...) {
     ...
     check =
     ...
  }
  return check;
}

И если результат шаблона не используется, то компилятор заменяет весь вызов на process_data(…), а не вычисляет check, который всё равно сразу будет выброшен.

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

Выше по этой ветке человек хотел такого: «произвольное поведение» должно быть ограничено как минимум правилом нанесения минимального ушерба, или критерием неразрушающего поведения.

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

Нет. Признак ЮБ, если этот код начал делать что угодно. А он просто в новой версии компилятора не компилируется или выбрасывает исключение. Это нормально.

Опять вы фантазируете. UB - это ситуации, которые стандарт никак не специфицирует, он говорит: «это не моё дело, реализация, делай с этим что хочешь». В такое определение вполне вписывается ошибка компиляции. И как это лихо вы вывели за скобки пример с делением, оно ведь ошибку не выдаёт, просто иначе стало работать, ЮБ как вы любите.

Не с вечными, а с теми, результаты которых не используются.

С вечными циклами это отдельная история, компилям разрешли их завершать, если они не производят сайд эффектов (ио, атомик, волотайл, …). GCC где-то до 10 версии этой ерундой не занимался и честно крутил вечные циклы, потом то ли моча в голову, то ли наркотики …

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

Если ЮБ, то это не значит, что ты даешь обезьяне гранату, ты получаешь какое-то разумное поведение, а не проход по корню диска и удаление файлов.

Как, по-Вашему, можно гарантировать разумное поведение после записи по произвольному адресу (причём по этому адресу в микроконтроллере даже код может лежать)?

UB - это ситуации, которые стандарт никак не специфицирует, он говорит: «это не моё дело, реализация, делай с этим что хочешь». В такое определение вполне вписывается ошибка компиляции.

Ошибка компиляции при UB тоже может быть. Но UB не обязывает ограничиваться ошибкой компиляции.

И как это лихо вы вывели за скобки пример с делением, оно ведь ошибку не выдаёт, просто иначе стало работать, ЮБ как вы любите.

Смена версии языка. В новой версии деление теперь определено так. Также как auto в C и C++ разное обозначают.

GCC где-то до 10 версии этой ерундой не занимался и честно крутил вечные циклы, потом то ли моча в голову, то ли наркотики …

Заранее неизвестно, что он вечный. Если не выкидывать вечные циклы, то практически никакие циклы выкидывать нельзя.

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

Как, по-Вашему, можно гарантировать разумное поведение после записи по произвольному адресу (причём по этому адресу в микроконтроллере даже код может лежать)?

неверно поставленный вопрос. актуальная форма вопроса такова - как можно гарантировать разумное поведение после бесконечного цикла в функции помеченной как [[noreturn]]?

ответ - просто делать свою работу. у такой функции есть инвариант

  • она не может вызывать return
  • исполнение не может достичь конца функции, при любых преобразованиях AST данной функции.

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

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

«функция пуста в силу ограничений спецификаций»

и тогда вы никогда не вызовите недостижимый код.

и вообще надо проверять инварианты функции после преобразований AST, если такие преобразования не дают строго тождественное дерево.

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

Как, по-Вашему, можно гарантировать разумное поведение после записи по произвольному адресу (причём по этому адресу в микроконтроллере даже код может лежать)?

А как гарантировать разумное поведение МК после того как я по нему молтком пи-ну? Это уже мои проблемы. От компилятора нужно, чтобы он не злоупотреблял оптимизациями и за каждую мою оплошность не бил меня звонко по щам, этот смысл и вкладывается в «ограничено как минимум правилом нанесения минимального ушерба, или критерием неразрушающего поведения». Нельлзя слишком в оптимизации заигрываться.

Смена версии языка. В новой версии деление теперь определено так.

Т.е. констатируем - питон - язык с ЮБ, хоть и скрывает это. Больше не тыкаем этой ЮБ скриптухой в плюсовиков.

Заранее неизвестно, что он вечный.

Ну естественно, что компиль должен доказать себе «вечность», да тут и к бабке ходить не надо, когда у тебя while (true)

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

А как гарантировать разумное поведение МК после того как я по нему молтком пи-ну? Это уже мои проблемы. От компилятора нужно, чтобы он не злоупотреблял оптимизациями и за каждую мою оплошность не бил меня звонко по щам, этот смысл и вкладывается в «ограничено как минимум правилом нанесения минимального ушерба, или критерием неразрушающего поведения». Нельлзя слишком в оптимизации заигрываться.

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

На этом зиждется высокая скорость программ на Си++.

Т.е. констатируем - питон - язык с ЮБ, хоть и скрывает это. Больше не тыкаем этой ЮБ скриптухой в плюсовиков.

Произвольного действия нет. Есть выбор одного из двух вариантов. В C++ это называется unspecified behavior.

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

ответ - просто делать свою работу. у такой функции есть инвариант

Эта функция содержит UB, поэтому инвариант у неё «делать что угодно» (потому что UB при любом выполнении функции).

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

Эта функция содержит UB, поэтому инвариант у неё «делать что угодно» (потому что UB при любом выполнении функции).

  1. функция имеет спецификацию - [[noreturn]] и ее некто не отменял и отменить не может.

  2. пустой цикл это не UB, а Dead Code. и удаляют его исходя их этого. при этом нарушая спецификацию [[noreturn]].

это просто баг и все. и UB вами тут притянуто за уши. Dead Code это не UB.

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

Тогда в ней обязан быть бесконечный цикл с побочными эффектами.

я уж показал, что такая функция, если она noreturn, должна быть соптимизирована до «пустой» в методом выкидывания команд от хвоста. после чего компилятор должен поставить в нее trap, поскольку войти в нее можно, она соптимизирована до пустой, то есть будет переход на след команду. и компилятор должен дать как минимум варнинг.

и тогда бага с переходом на недостижимый код исчезнет, и все будет строго.

если такая функция вызовется - будет trap. как с абстрактным методом.

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

так должно происходить ВСЕГДА. потому что тут действует примитивная логика ненарушения обьявленного инварианта.

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

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

На этом зиждется высокая скорость программ на Си++.

А вы не спешите с такими выводами, скорее всего они ошибочны. ЮБшные оптимизации вызывают улыбку, если честно. Например, кейс bugfixer’a, где получается вечный цикл, вырезка вечных циклов (спасибо, но их и так нет в норме), проверка указателей после чтения … . Есть ощущение, что основных оптимизаций всего несколько (первое место - это инлайн). Есть софт для спекулянтов на форе - metatreader, так вот ее авторы сделали свой, похожий на кресты язык, но только он для самых маленьких, прощает все ошибки, и никакого ЮБ там нет, которое вылезет при оптимизациях, все максимально стабильно. Так вот они делали всякие тесты и заявляли, что очень близко приблизились к крестам. Мораль такова - самые маленькие и говнокодеры могут подобрать свой набор активных оптимизаций и иметь вполне быстрый код.

Произвольного действия нет. Есть выбор одного из двух вариантов. В C++ это называется unspecified behavior.

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

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

так должно происходить ВСЕГДА. потому что тут действует примитивная логика ненарушения обьявленного инварианта.

UB выше объявленного инварианта.

Например, код

[[ noreturn ]] void fn() {
   int *p;
   volatile int x;
   *p = SOME_CONST;
   while (true) x = 1;
}

без оптимизаций может при записи испортить код, находящийся после *p = 1 и сделать цикл небесконечным.

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

metatreader, так вот ее авторы сделали свой, похожий на кресты язык, но только он для самых маленьких, прощает все ошибки, и никакого ЮБ там нет, которое вылезет при оптимизациях, все максимально стабильно

Где про него прочитать, как в нём происходит адресная арифметика и работа с динамической памятью?

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

Как проверить? А то люди заявляют, что написали компилятор питона, который компилирует в код в два раза быстрее Си++.

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

при записи испортить код, находящийся после *p = 1

в каком смысле испортить? там будет защита кодового сегмента, если вы имеете ввиду изменение прямо самого кода программы через запись по указателю.

также можно сделать опцию - запись trap в конец noreturn функции, ровно также как с защитей при вызове абстракного метода.

то есть при правильном подходе к вопросу сохранения инварианта его решить можно. а при неправильном можно испортить что угодно.

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

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

Всё сломает = старый язык заброшен, а на его основании создан новый. Но создание Си++ не привело к уничтожению Си, а создание Питона3 к уничтожению Питона2.

$ python2.7
Python 2.7.18 (default, Aug  1 2022, 06:23:55)
[GCC 12.1.0] on linux2

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

А unspecified behavior может быть по-разному в пределах одной версии языка программирования.

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

Где про него прочитать, как в нём происходит адресная арифметика и работа с динамической памятью?

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

Вот слова Рената Фатхлина (он там главный):

сентябре релизим новый компилятор MQL5, который генерит очень эффективный код для МТ5 х64. Он позволит приблизиться к скорости чистого C/C++. Ускорение с текущей версией компилятора MQL5 от 2 до 10 раз. Это означает, что можно отказываться от DLL в тяжелых расчетах.

Где-то и тесты есть, но я не вижу смысла не верить.

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

Где-то и тесты есть, но я не вижу смысла не верить.

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

Он позволит приблизиться к скорости чистого C/C++.

Приблизиться, а не сравняться. И это после ускорения в 10 раз…

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 2)
Ответ на: комментарий от monk

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

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

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

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

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

Работа через дескрипторы не может давать такую же скорость, как через сырые указатели

если там llvm, она мощно все соптимизирует и будет по сути работать с сырым указателем, взяв его только раз где надо.

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

если там llvm, она мощно все соптимизирует и будет по сути работать с сырым указателем, взяв его только раз где надо.

Не может. Дескриптор выглядит так

struct 
{
  bool valid;
  T* p;
}

И вместо

*p = 1

получается

if(p.valid) { *p.p = 1; } else terminate(); 

Выкинуть проверку на valid llvm не может. Разве что в одном блоке несколько обращений подряд по одному дескриптору. И с массивом аналогично. Если в С++ использовать at, программа работает заметно медленнее.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

if(p.valid) { *p.p = 1; } else terminate();

решение странное.

пусть уберут valid и нулят поинтер. обращение по nil будет радостно перехватываться своим хандлером и будет то же самое. только без лишних проверок

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

пусть уберут valid и нулят поинтер. обращение по nil будет радостно перехватываться своим хандлером и будет то же самое.

Может и так, тогда просто дополнительная операция косвенного доступа к памяти, так как эта структура должна копироваться по ссылке.

Но именно в этом языке у дескриптора три статуса: https://www.mql5.com/ru/docs/constants/namedconstants/enum_pointer_type

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

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

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

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

Нет там мусоросборки. Но есть гарантированное завершение приложения при обращении по ошибочному (освобождённому) указателю.

При наличии мусоросборки UB уже не нужен, но скорость Си++ недостижима.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от unDEFER

Ну или вот краткий гайд, какие в принципе есть пути решения задачи полиморфизма в расте https://www.brandons.me/blog/polymorphism-in-rust

А у же про то как писать/использовать трейты, а так же dyn типы, в документации вроде бы написано подробно

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

Единственный пример с конструктором где N=70 - инициализировать можно только 64 бита,

У тебя у процессора разрядность какая? Поставь себе проц на 128 бит, компилятор и делай 128.

А почему по нормальному нельзя инициализировать?

Возьми плюсы и сделай по нормальному. Выделяешь массив байт, по индексу / 8 высчитываешь индекс в массиве, потом сдвигаешь 1 на остаток и делаешь & с байтом из массива. Получаешь значение. Чтобы установить делаешь всё наоборот. Скобки там перегружаются, всё нормально.

crutch_master ★★★★★
()

C++ и Rust имеют очень странные конструкторы для битовых массивов. Может это проблема документации, но я с ходу не нашёл как мне создать битовый массив из готового байтового массива, чтобы каждый байт превратился в 8 бит.

В C++ достаточно использовать std::vector<T> из заголовочного файла <vector>. Он будет оптимизирован для работы с типом bool во время компиляции. std::vector<bool> some_dimension(32, 0); будет занимать всего 4 байта (на самом деле это зависит от реализации STL, будете ли вы использовать методы, будет ли он const или нет, и т.д.), но суть в том, что оно будет оптимизировано.

https://cplusplus.com/reference/vector/vector-bool/

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

Лично я не понимаю, нафига нужен битовый массив. Это может быть полезно при программировании микроконтроллеров, решении «олимпиадных задач» по программированию, но вот зачем на десктопе тормозить работу программы, чтобы сэкономить чуть-чуть памяти – непонятно.

Да, если кто не в курсе, битовый массив работает так: Обращаемся к (N/8)ому элементу массива, потом делаем N % 8 сдвигов влево, потом берем старший бит.

zx_gamer ★★★
()