Написанное дальше содержит мнение о языках программирования, которое вам может не понравиться. Я предупреждал.
Что мне нужно от языка
Некоторые требования не подлежат обсуждению или поиску компромиссов. Во-первых, язык должен быть надёжным. Я не могу позволить себе тратить своё время на поиск ошибок, которых я не совершал.
Я написал большое количество игр на Flash, и вот, Flash умер. Я не хочу тратить время на портирование старых игр на современные платформы, я хочу писать новые игры. Мне нужна платформа, в долговечности которой я буду уверен.
Чего я точно хочу, так это избежать зависимости от определённой ОС. В идеале — иметь возможность разрабатывать и для консолей. Поэтому важно, чтобы язык позволял легко портировать написанное на нём, а также имел хорошую поддержку кроссплатформенных библиотек.
Чего я хочу от языка
Самым важным, но не обязательным требованием является простота. Изучение особенностей языка и и его чудного «умного» API я нахожу безумно скучным. Идеальный язык можно однажды изучить и никогда больше не пользоваться его документацией.
Поиск и исправление багов истощает творческий потенциал. Я хочу оставлять меньше багов, потому желаю строгую типизацию, качественные сообщения об ошибках и статический анализ кода. Хочу упростить поиск ошибок, а, следовательно, хорошие отладочные средства и динамический анализ.
Меня не интересует красивая реалистичная картинка, зато я забочусь о производительности. Меньшая требовательность к ресурсам расширяет спектр возможностей, доступных для реализации. Особенно интересно наблюдать за тем, что происходит с современным мощными компьютерами, когда разработчик о производительности не думает.
Ещё больше меня волнует скорость компилятора. Я не какой-нибудь буддийский мастер концентрации, и ждать больше 10 секунд — расточительство. Хуже того, это выбивает из потока. Вроде только глянул Twitter, а 5 минут куда-то пропали.
Я не адепт ООП. Большую часть времени, проведённого за работой, я имел дело с классами и объектами. Но чем дальше, тем меньше я понимаю, почему надо так жёстко объединять код и данные. Я хочу работать с данными, как с данными, и писать код, который лучше всего подходит в конкретной ситуации.
Альтернативы
C++ продолжает оставаться наиболее распространенным языком для разработки игр, и на то есть свои причины. До сих пор большинство моих заказных проектов пишется на нем, и это мне не нравится.
C++ покрывает мои нужды, но не соответствует желаниям. Он дико сложный. Несмотря на наличие годных инструментов, на нём легко допускать коварные ошибки. Кроме того, по сравнению с C, он медленно компилируется. C++ имеет высокую производительность и предоставляет возможности, которых нет в C, но это не то, чего я хочу, да и достигается это ценой большой сложности.
C# и Java имеют схожие проблемы. Это многословные и сложные монстры, а мне нужен маленький простой зверёк. Оба языка направляют программиста прямиком в пучину ООП, а я против этого. Как и в большинстве высокоуровневых языков, многие сложные вещи тут скрываются так, что ничего не мешает случайно выстрелить себе в ногу.
Мне очень нравится Go. Во многих аспектах это изобретённый заново C, с поправкой на то, что до представления его публике он продумывался несколько лет. Я хотел бы использовать Go, но есть один огромный подводный камень — сборка мусора. Разработка игр на Go сомнительна, ведь сборщик мусора будет приостанавливать весь игровой мир, чего разработчик не может позволить. Также тут всё не очень хорошо с игровыми библиотеками. И хотя всегда можно приспособить для этого дела библиотеку на C, причём без особых проблем, это всё равно порождает много лишней работы. Кроме того, у меня есть сомнения насчёт перспектив. Go был бы неплох для веба, но это стремительно меняющаяся среда. Особенно это почувствовалось со смертью Flash.
JavaScript мне абсолютно не нравится. Он предоставляет столько свободы, что мне непонятно, как люди умудряются писать сколько-нибудь сложные проекты на нём. И не хочу даже пытаться его пробовать.
Haxe выглядит гораздо перспективнее остальных языков в этом списке. У него нет проблем с библиотеками. Если я снова начну писать под веб, то обязательно познакомлюсь с ним поближе. Несколько беспокоит относительная молодость языка, будет ли он жить? Больше мне добавить нечего, с Haxe я успел лишь немного поиграться, не углубляясь.
Джонатан Блоу пишет свой язык. Язык, которым он сам захотел бы пользоваться. Я восхищаюсь его решением, иногда и сам загораюсь идеей поступить так же. Но не выкинуть же все существующие библиотеки. А реализовать полную с ними совместимость — не так просто. Да и вообще это трудно, я бы предпочёл дальше писать игры, а не языки программирования.
Почему C — лучший выбор для меня
Хоть C и опасен, зато надёжен. Это очень острый нож, способный отрубить пальцы так же легко, как нарезать овощи. Но он прост, и научиться правильно его использовать труда не составит.
Он быстрый. А когда дело доходит до компиляции, я и представить себе не могу, что какой-то язык способен сделать это быстрее.
Есть возможность писать код так, чтобы он работал везде. И обычно это относительно несложно. Трудно представить, что однажды это изменится.
Есть отличная поддержка библиотек и инструментов.
Хоть меня это и несколько печалит, но C до сих пор остаётся лучшим языком для меня.
Я совсем не хочу сказать что-то вроде: «Эй, вы тоже должны писать на C». Я осознаю, что мои предпочтения весьма специфичны. Кроме того, по количеству, написанного мной на разных языках, код на «vanilla» C занимает лидирующую позицию, так что это уже часть моей зоны комфорта.
Так что да, C для меня — лучший выбор.
От переводчика
Перевод местами достаточно вольный, но не в ущерб смыслу или содержанию.
Подготовил перевод специально к пятнице, в этот день он особенно уместен, на мой взгляд. Хоть автор оригинального текста и пишет, кажется, на полном серьезе… вы сами всё понимаете, если прочитали. Как истину прочитанное воспринимать не стоит.
С предложениями, пожеланиями и замечаниями, как обычно, в ЛС.
Комментарии (251)
tmnhy
15.01.2016 00:24+6А где сами игры то? Хоть на Си, хоть на Go…
gwer
15.01.2016 00:25+2На сайте автора: jonathanwhiting.com
alekciy
15.01.2016 09:56+5Мдя… игры выглядят как архтефакт 80-90-ых. Как то сразу вспомнились 8 бит и денди.
EvilPartisan
15.01.2016 12:29+5Ныне очень модное явление в Steam. Но многие из них, даже с такого уровня графикой, умудряются как-то многовато ресурсов жрать.
gwer
15.01.2016 12:30+2Потому что написаны на чём? Правильно, на чём угодно, но не на C. И даже не на плюсах наверняка. «Мы же пишем как бы старую игру на новые машины, тут производительность огого!»
KumoKairo
15.01.2016 12:45+3Очевидно, что на разных языках тоже можно писать по-разному, забывая про оптимизацию. Я вполне уверен, что на С тоже можно написать медленного монстра. Винить в плохой производительности только сам язык (компилятор, рантайм), оторванно от контекста и конкретных решений (в том числе и архитектурных), по меньшей мере сомнительно.
gwer
15.01.2016 12:50+4Медленного монстра можно написать на чём угодно. Быстрого — нет. Впрочем, это вопрос задачи. Нередко для части, требующей хорошую производительность, пишут отдельный модуль на более подходящем языке.
NElias
19.01.2016 23:44+1Язык здесь не при чем. Оптимизация ведется на другом уровне — модели, текстуры, хитрые подмены 3d на 2d и прочие трюки. Ну а самое главное шейдеры — скелетная анимация, свет, тени и прочее. Сейчас большинство вычислений в играх делает на GPU.
CodeRush
15.01.2016 00:43+53Ну что сказать, это личный кактус отдельного человека, и он готов его кушать.
Я пишу на C потому, что именно на нем весь UEFI и написан, но я с удовольствием перешёл бы на Rust прямо завтра, если бы такая возможность была, т.к. от некоторых особенностей C уже мутит.
Синтаксис указателей на функции понимают два с половиной специалиста, молчаливое приведение типов приводит к очень хитрым ошибкам, от которых не спасает даже статический анализатор, практически все функции для работы с форматной строкой — одна большая дыра в безопасности, забытый volatile моежт обрушить цивилизацию в самом неожиданном месте, а макросами можно отстрелить себе не то, что ногу, а вообще все и несколько раз. Добавим сюда ручное управление памятью, которое почти никто не умеет делать правильно, буфера постоянного размера на стеке, и великий и ужасный NULL — изобретение на минус миллиард долларов. Несмотря на все это, С — прекрасный кроссплатформенный ЯП низкого уровня, и на низком уровне ему альтернатив практически нет. Подходит ли он для игр — надо спросить у автора, если он когда-нибудь вылезет из отладки.monah_tuk
15.01.2016 07:39+6Синтаксис указателей на функции понимают два с половиной специалиста
да что тут такого:
void (*variable)(int, int);
или
вовзращаемое_значение (*имя_переменной)(аргументы);
для объявления типа всё то же самое:
typedef вовзращаемое_значение (*имя_типа)(аргументы);
или через using в C++11:
using имя_типа = вовзращаемое_значение (*)(аргументы);
С указателям на методы класса (C++) чуточку сложнее, но тоже не rocket science, просто перед "*" добавляется Clazz::
Правда с вызовом чуточку по другому:вовзращаемое_значение (Класс::*имя_переменной)(аргументы); typedef вовзращаемое_значение (Класс::*имя_типа)(аргументы); using имя_типа = вовзращаемое_значение (Класс::*)(аргументы);
Clazz foo; (foo.*some_variable)(parameters); Clazz *bar = new Clazz; (bar->*some_variable)(parameters);
В обоих случаях нельзя хранить указатели в void*, как бы того ни хотелось — это отличие от обычных указателей нужно запомнить, хотя компилятор должен выдать предупреждение.CodeRush
15.01.2016 08:17+15Все хорошо, да. А теперь то же самое, но для функции, которая сама указатель на другую функцию принимает, как qsort из стандартной библиотеки. Необходимость в typedef сразу же указывает на проблему, а если его не использовать, получится дикая смесь из скобок, запятых и имен типов, которую невозможно ни толком прочитать, ни толком понять.
Плюс еще нужно о соглашении о вызовах не забыть, если предполагается, что программа будет использоваться на голом железе, причем ключевое слово для него не стандартизировано и потому выглядит как макрос EFIAPI, который раскрывается либо в "", либо в __cdecl, либо в __attribute__((cdecl)), либо еще в какой-нибудь __кошмар.
Честно сказать, я не знаю, как нужно было сделать лучше. Но тот факт, что указатели на функции в их нынешнем виде мало кто понимает — вижу на работе стабильно раз в месяц.monah_tuk
15.01.2016 09:32+2а если его не использовать, получится дикая смесь из скобок, запятых и имен типов, которую невозможно ни толком прочитать, ни толком понять.
почти Lisp :-)
Плюс еще нужно о соглашении о вызовах не забыть, если предполагается, что программа будет использоваться на голом железе
Мне кажется, что это к C, как таковому, имеет сильно посредственное отношение: трудно стандартизовать то, что может поменяться. Вдруг кто напишет OS, где будет принято аргументы в функцию передавать через один: через стек, через регистр, через стек, через регистр, или передавать адрес возврата первым аргументом. Или последним. Или в середине. Ну и тяжёлое наследие x86. На x86_64 как бы не так много вариантов оставили. Ну и другие платформы есть.
Но тот факт, что указатели на функции в их нынешнем виде мало кто понимает — вижу на работе стабильно раз в месяц.
Вот тут, похоже, я встретился с тем, чего я не знаю или не совсем понимаю. По сути, какое отношение имеет указатель на функцию с соглашениями на вызов? Ну и примеры недопонимания можно привести, без вреда NDA и прочему POPI.EvilPartisan
15.01.2016 09:46+3А чем вам typedef не нравится?
CodeRush
15.01.2016 11:09+2Мне он не нравится тем, что я вынужден проходить цепочку из макросов и typedef'ов, чтобы понять, что это за зверь вообще и как он будет выглядеть из окна аппаратного отладчика. Т.е. одно дело, когда на тебя из этого окна смотрит целая переменная со значением 17, и совсем другое — когда это не целая переменная, а указатель на целую, или на функцию, что еще хуже.
Сам по себе typedef — это благо, а проблемы такого рода решаются грамотными IDE, но они от этого не перестают быть проблемами.
CodeRush
15.01.2016 10:53+5Я тут рассматриваю не только язык сам по себе, но и все, что вокруг, поэтому директивы компилятора, разные #pragma и прочие __attribute__((declspec)) я тоже отношу к C, просто не к чему больше. Не спорю, опять же, что стандартизировать такое сложно, однако #pragma STDC ... как-то пробрались в C99, значит не все еще потеряно в этом смысле.
По поводу отношения соглашения о вызовах к указателям на функцию, простой пример: все компоненты UEFI находятся в одном адресном пространстве и общаются путем прямого вызова функций через таблицы указателей на них (такие таблицы называются «протоколами»). При этом, для архитектуры amd64 существует два распространенных соглашения о вызовах: Microsoft x64 (которое и используется в 64-битном UEFI и которое использует по умолчанию компилятор MSVC) и System V amd64 ABI, которое используют
*nix-системы и которое по умолчанию использует GCC. При этом компоненты UEFI не собираются одним и тем же компилятором, и зачастую поставляются в бинарном виде, поэтому очень важно, чтобы соглашение о вызовах было одинаковое, а это достигается добавлением EFIAPI ко всем typedef'ам всех указателей на функции, являющиеся частью протоколов, к прототипам и реализациям этих функций.
Пример кода#include "Uefi.h" // Define SIO DXE protocol GUID #define EFI_CRSIO_DXE_PROTOCOL_GUID { 0xc38bdbd, 0x6f6b, 0x49ae, 0x99, 0x5a, 0x1d, 0x6c, 0xc2, 0x9d, 0x95, 0xa1 } // Extern the GUID for protocol users. extern EFI_GUID gEfiCrSioDxeProtocolGuid; // Forward reference for ANSI C compatibility typedef struct _EFI_CRSIO_DXE_PROTOCOL EFI_CRSIO_DXE_PROTOCOL; // Define each protocol interface function and the respective function pointer type typedef UINT16 (EFIAPI *CRSIO_DXE_GET_INDEX) ( //Если вот здесь забыть EFIAPI, ничего не сломается, т.к. функция не имеет параметров. VOID //А вот если бы они были, а компилятором оказался GCC, то он бы их положил в RDI, RSI... ); //а функция их ждет в RCX, RDX..., т.е. на вид все хорошо, а по факту в регистрах мусор вместо параметров UINT16 EFIAPI // А если забыть его вот здесь, не забыв выше, будет почти то же самое, только упадет все в другом месте CrSioGetIndex( VOID ); // Define protocol data structure typedef struct _EFI_CRSIO_DXE_PROTOCOL { CRSIO_DXE_GET_INDEX CrSioGetIndex; } EFI_CRSIO_DXE_PROTOCOL;
monah_tuk
17.01.2016 04:48+2А! Теперь понял, вы про информацию о соглашении на вызов в самой сигнатуре при объявлении переменной или типа.
MacIn
16.01.2016 02:06+5А теперь то же самое, но для функции,
Вроде такого (со SO):
Consider the signal() function from the C standard:
extern void (*signal(int, void(*)(int)))(int);
Perfectly obscurely obvious — it's a function that takes two arguments, an integer and a pointer to a function that takes an integer as an argument and returns nothing, and it (signal()) returns a pointer to a function that takes an integer as an argument and returns nothing.
webhamster
15.01.2016 09:01-2>> Синтаксис указателей на функции понимают два с половиной специалиста
> да что тут такого:
> void (*variable)(int, int);
И сразу вопрос из зала: где тут имя функции?monah_tuk
15.01.2016 09:33+7Именно функции или переменной в которой можно сохранить указатель на функцию? ;-)
AllexIn
15.01.2016 07:58+4ручное управление памятью, которое почти никто не умеет делать правильно
Два простых правила, чтобы забыть о проблемах с ручным управлением памятью:
1) удаляем память там же где и создаем
2) следующая вещь, которая пишется после new — это delete
В тоже время особенности работы GC для каждого отдельно языка содержат многостраничные мануалы о том, как правильно управлять памятью, как делать нужно и как делать не нужно… Не говоря уж о тонне костылей, типа андроидовского Out of Memory, если у Bitmap не вызвать recycle.CodeRush
15.01.2016 08:29+14А что делать с обработкой ошибок, копировать все вызовы free() в каждый if (EFI_ERROR(Status)) или использовать goto fail, или может быть do { if (error_condition) break; } while(0), если у вас на goto аллергия?
Если бы простыми правилами можно было бы обойтись, память не текла бы у практически всех программ на С сложнее hello.c. Язык не виноват, но люди устроены именно так.monah_tuk
15.01.2016 09:41+2А по мне, так это, пожалуй, единственный вариант где goto применим. Отчего аллергия-то? Ну или сделать свой аналог try/throw/catch/finally при помощи setjump/longjump если вообще сыпью покрываетесь :-)
CodeRush
15.01.2016 11:02По мне тоже, но случается и вот такое, причем у всех, даже у самых гурушных гуру. У нас там все проще в UEFI — прошивка отрабатывает быстро и умирает быстро, а всю память, выделенную как EfiBootServicesCode или EfiBootServicesData освободит менеджер BDS, просто пометив ее свободной в UEFI MemMap, и ее потом перепишет ОС, когда ей понадобится. Т.к. мало кто в прошивке пытается выделить себе с кучи гигабайт-другой, то вся система довольно спокойно отностится к утечкам — в конце все равно останутся только пара рантайм-сервисов, для которых управление памятью уже написано и работает нормально.
webhamster
15.01.2016 09:12> Два простых правила, чтобы забыть о проблемах с ручным управлением памятью:
> 1) удаляем память там же где и создаем
> 2) следующая вещь, которая пишется после new — это delete
А что делать с событийной моделью, когда например работаешь с буфером обмена? В обработчике drag создается объект, указатель на который пихается в буфер, При drop объект должен быть удален. Получается что new и delete расположены вообще в разных местах. Ваши правила не работают.monah_tuk
15.01.2016 09:38+2Для сложных случаев использовать подход, типа gobject с счётчиками и _ref/_unref? Ну как вариант.
AllexIn
15.01.2016 10:25+5Ваши правила не работают.
Очевидно, что правила не работают, если их неправильно применять.
Зачем вы в Drag суете указатель? Почему не сам объект, например? Он слишком большой? Тогда надо передавать не указатель, а его владельца с индексом, например.
crmMaster
15.01.2016 10:13+5В C нет new и delete, там malloc и free — те еще кактусы, особенно в комплекте с привычкой C неявно приводить типы.
mustitz
15.01.2016 23:50+1У меня другие правила: (1) использовать valgrind, (2) функциональные тесты под valgrind.
R10
17.01.2016 17:17+4В современном С++ new и delete не используются. Smart pointers (unique_ptr), RAII, move семантика решают проблемы с памятью. Проблема не с памятью, а с ownership, shared state и object lifetime. Многие проблемы «старого» C++ (до C++11) решены, но по-прежнему использование С++ требует высокой квалификации программиста. Фишка С++ в том что на нем можно писать как высокоуровневые вещи (в отличие от С) так и очень низкоуровневые (в отличие от Java/C#). Поэтому в game dev С++ основной язык. Rust, Go, D как системные языки — все еще несколько сыроваты на настоящее время.
vladon
18.01.2016 14:41+1Два простых правила, чтобы забыть о проблемах с ручным управлением памятью:
1) удаляем память там же где и создаем
2) следующая вещь, которая пишется после new — это delete
А если между new и delete возникает исключение?
Два простых правила:
1) Не используем new и delete (если мы не писатели низкоуровневых либ)
2) Используем умные указателиAllexIn
18.01.2016 14:46+1Вы так говорите, как будто new и delete должны быть в одном методе.
Если они в одном методе — они вообще не нужныю
«Там же» — в том же логическом пространстве.
По поводу smart_ptr… Холивар будет. :) Так что пожалуй не буду ничего писать.vladon
18.01.2016 14:48+1С чего вы взяли, что я говорю, что они в одном методе? Я говорю между вызовами new и delete может возникнуть исключение.
Напишите, что вас не устраивает в умных указателях, интересно же. Не холиварно, а конкретно.AllexIn
18.01.2016 14:59+1А почему конкретно «исключение»?
Может произойти тысяча вещей, которые поломают логику и эти вещи надо учитывать.
Если исключение поломало нам все, то уже пофиг на утечку будет. Если же мы исключение нормально обработали или штатно подавили — оно не повлияет на дальнейший delete.
По поводу не холиварно, а конкретно — все холивары начинаются с конкретных высказываний. :)
Ну ладно, один конкретный пример: нужно учитывать циклические ссылки и держать весь путь мигрированя указателя, чтобы не допустить цикличности. По сути это слабо отличается от необходимости держать такой же путь для обычного указателя, чтобы предсказуемо его удалить.vladon
18.01.2016 15:02+1Исключение можно обрабатывать и позже, но leak ресурса уже возникнет. А так — RAII сработает при unwind'е.
Ну ладно, один конкретный пример: нужно учитывать циклические ссылки и держать весь путь мигрированя указателя, чтобы не допустить цикличности. По сути это слабо отличается от необходимости держать такой же путь для обычного указателя, чтобы предсказуемо его удалить.
Вы сейчас конкретно про shared_ptr.
vladon
18.01.2016 15:04+1Ну ладно, один конкретный пример: нужно учитывать циклические ссылки и держать весь путь мигрированя указателя, чтобы не допустить цикличности. По сути это слабо отличается от необходимости держать такой же путь для обычного указателя, чтобы предсказуемо его удалить.
И, кстати, вы таким образом переизобретёте shared_ptr.
potan
15.01.2016 18:42+2В Rust хорошая интероперабельность с C (но не с C++). Может пора попробовать его использовать?
CodeRush
15.01.2016 19:33+3Уже однажды попробовал и не смог.
Среда UEFI достаточно сильно отличается от привычной стандартной библиотеки, к примеру, там почти все строки — в UCS2, и уже одно это приносит кучу боли, т.к. строковые литералы просто как L«String» теперь не запишешь — в Rust UTF8 везде. Плюс абсолютно всё получается внутри unsafe {}, и в итоге преимуществ у Rust почти не остается, а недостатки вроде необходимости писать врапперы к библиотекам и протоколам — никуда не деваются. Я видел пару попыток, но их авторы тоже бросили, практически не начав.
Если время найду, попробую еще раз.
ToSHiC
15.01.2016 19:39+1Кстати, а C++ без RTTI и stl имеет смысл использовать, сделав внешний интерфейс на чистом C, или слишком много внешних вызовов? По сути, использовать от C++ возможность работать с объектами, а не структурами (RAII для управления памятью/ресурсами), и шаблоны.
CodeRush
15.01.2016 19:45+1Не получится, т.к. нет рантайма (т.е new, delete и прочие красивости C++ просто не работают), а без него C++ мало чем лучше C. Вот тут Count Chu рассказывает, как он заводил код на C++ в EDK2, но успехом я такое решение точно не назову.
ToSHiC
15.01.2016 20:11+1Можно же сделать operator new (и остальные 3 штуки), которые будут звать malloc/free из «рантайма», который у вас уже есть. Или тоже не катит?
CodeRush
15.01.2016 20:40+2Катит, но там не только их не хватает, а в итоге нет ни исключений, ни стандартной библиотеки, и непонятно, зачем вообще огород городить, если на C уже все готово. В Rust хотя бы borrow checker поможет и иммутабельность по умолчанию, а чем хорош C++ в данном случае я просто не знаю.
0xd34df00d
15.01.2016 20:58+2Note that C++ classes and templates (e.g. std::vector, std::string) actually aren't part of the C++ language. They are part of a library called the Standard Template Library.
Подобные противопоставления очень сильно снижают доверие к источнику.CodeRush
15.01.2016 21:13+1Тем не менее, даже гугловская libstdc++ не поддерживает STL, так что в каком-то смысле они тоже правы.
0xd34df00d
16.01.2016 17:18+1А ниже написано:
This runtime is an Android port of STLport (http://www.stlport.org). It provides a complete set of C++ standard library headers. It also, by embedding its own instance of GAbi++, provides support for RTTI and exception handling.
(курсив наш)
И кому верить?CodeRush
16.01.2016 22:52+1Всем, libstdcpp и STLPort — два разных рантайма с разными же возможностями (и размерами), первый предосталяет только некоторые функции, а второй — всю стандартную библиотеку.
R10
17.01.2016 17:22+1В игровых движках на С++ как правило используют свои контейнеры и строки. STL используется, но в ограниченном объеме. Есть специально заточенные STL реализации типа EASTL, tinySTL.
0xd34df00d
17.01.2016 21:06+1Безусловно. Но это не лишает STL статуса части стандарта, а значит, и части языка.
ToSHiC
15.01.2016 21:01+1Блин, вот про исключения то я забыл, без них совсем печаль, да.
0xd34df00d
15.01.2016 21:03+1Нормально без них и даже хорошо, я в своём коде вообще стараюсь уходить от исключений к монадической Either-подобной обработке ошибок.
SAKrisT
15.01.2016 00:47-18Автор зазнайка? Или это такой стиль? Везде «Я», в оригинале «I».
Как-то тяжело читать.gwer
15.01.2016 00:53+7Английский язык, по моим наблюдениям, часто страдает излишними повторами слов, которые в русском либо просто не очень хорошо выглядят, либо наводят на подобные мысли. А вообще, автор же лично о себе пишет.
Iceg
15.01.2016 11:36+7Английский язык совсем не страдает от этого :) А вот переводы на русский — почти всегда.
dannyzubarev
15.01.2016 02:38-14Англичане жуткие собственники и частое повторение «I» в речи – абсолютно нормальное явление.
burjui
15.01.2016 14:29+3А, мне кажется, это связано с меньшим количеством падежей и гораздо меньшим их использованием в английском языке. В русском языке ситуация иная: во-первых, «я» склоняется по падежам (+ меня, мне, мной; в английском только + me), а во-вторых, местоимения часто можно опускать, т.к. их с успехом заменяют суффиксы глаголов (работаю, работаешь и т.п.).
k12th
15.01.2016 00:50+9Ну, SDL-то написан на C, так что ничего особо удивительного в написании игр на нем нету.
Меня не интересует красивая реалистичная картинка, зато я забочусь о производительности.
А нахрена производительность в играх, кроме как на красивую картинку?:)Voiddancer
15.01.2016 05:30+5k12th
15.01.2016 11:44-2Таких игр — примерно одна на все остальные, и ОП не входит в число ее создателей, так что не считово:)
poxu
15.01.2016 12:10+2Таких игр — примерно одна на все остальные
Minecraft, Terraria, Starbound, Gnomoria, Towns, Timber and stone, KeeperRL (open source кстати), CDDA.
Это первое, что в голову пришло. А их ещё много.
AllexIn
15.01.2016 07:59+6SDL написан на С, чтобы его можно было подключить к любому языку.
gurinderu
15.01.2016 12:00+4Я думаю это не аргумент, с таким же успехом можно было написать SDL на с++.
Я согласен в чем-то с автором, т.к. C очень хорош именно для игр.
Весь opengl api это С, shaders тоже по сути C-like syntax. Так зачем использовать зоопарк языков, когда все можно писать на одном.AllexIn
15.01.2016 12:05+3Написать на С++ и потом городить экспорт?
Тем более SDL — набор отдельных модулей с низкой связанностью, без ООП там вполне можно жить.
Я согласен в чем-то с автором
Ну а я не согласен. Просто не могу представить архитектуру сколь-либо сложной игры на plain С. Может у меня заболевание мозга уже и я не мыслю программирования без ООП. Но весь опыт проектирования говорит мне, что С++ код при прочих равных будет чище аналогичного кода на plain C в сложной игре.
Весь opengl api это С, shaders тоже по сути C-like syntax
Поэтому все первым делом и пишут ООП обертку над OpenGL и шейдерами. :))
Например, я не могу представить как будет выглядеть мультирендер на plain C.
На С++ это просто абстрактный интерфейс и отдельная реализация для каждого рендера.
А на plain C?iCpu
15.01.2016 12:22+1Указатель на функцию? Я не защищаю C, но всё, что нужно помнить про ООП в C++ — это обёртка, скрывающая указатели от конечного программиста.
AllexIn
15.01.2016 12:32+1То есть лучшее решение — эмулировать ООП, вручную устанавливая указатели.
iCpu
15.01.2016 12:39«Эмулировать ООП» — это слишком сильный и смелый термин. В данном контексте даже неверный, ведь нужно просто хранить адрес следующей инструкции.
Скорее обратно, ООП приходится реализовывать через такие грязные методы. Опять же, C++ — это не полноценный ООП, так как нету полноценных сигналов-слотов. Qt куда ближе, но он реализован через ещё более грязные методы: и строковые описания, и генерация методов, и куча чего ещё.AllexIn
15.01.2016 12:41+5А почему вы считаете что слот-сигнальная система является неотъемлемой частью настоящего ООП?
iCpu
15.01.2016 12:43-3Ну, возможно, потому, что она таковой и является? Да, я использовал неверный термин, объекты обмениваются сообщениями, но где в C++ сообщения, без Qt или бустовой магии, которую ещё собрать нужно?
AllexIn
15.01.2016 12:47+7class cA{ public: foo(); }; class cB{ cA* m_a; bar(){ m_a->foo(); } }
Здесь есть обмен сообщениями, но нет слот-сигналов. Откуда вообще мысль, что обмен сообщениями привязан к слот-сигнальной системе и не может быть реализован другими методами?iCpu
15.01.2016 12:48-1Броадкаст сообщение покажите?
AllexIn
15.01.2016 12:49+6Откуда мысль что бродкаст является обязательной частью ООП?
iCpu
15.01.2016 12:54-5Не является. Но, например, передача сообщений между потоками — является. Удачи с доказательством.
AllexIn
15.01.2016 12:55+2Брр. Вы сейчас говорите, что два потока на С++ не могут обмениваться сообщениями? :)
iCpu
15.01.2016 12:57-1Не в том виде, что вы указали.
AllexIn
15.01.2016 13:11+4Так в том и фишка. Нет никаких требований к способу обмена. Обмен есть, остальное не имеет значения.
iCpu
15.01.2016 13:28+3Если так подходить к вопросу, вы правы. Но тогда не вижу причин не причислять да даже тот же чистый C к ООП языкам. При желании можно даже наследование сделать и много чего другого интересного сделать, года два назад были подобные статьи.
Я очень уважаю ваше желание отстоять звание C++, но взгляните правде в глаза. Это не то ООП, которое создали вместе со SmallTalk.AllexIn
15.01.2016 13:32+6Я не против других подходов к определению ООП. Но мне пока не понятно откуда вы берете свои доводы.
Определенно C++ не SmallTalk. Но ему им быть и не надо.
И на plain C можно эмулировать ООП. Также как на С++ можно эмулировать слот-сигнальную систему.
Вы подняли сейчас верную тему. ООП — методология проектирования, я не свойство языка. Просто есть языки, на которых эта парадиграм реализуется просто, а есть те, на которых сложно. И всё.
Googolplex
15.01.2016 12:49+3Строго говоря, здесь нет обмена сообщениями. Здесь вызов функции, как в старом добром процедурном языке, просто выглядит иначе. Обмен сообщениями подразумевает, что та сторона, которой отправлено сообщение, сама решает, что делать с сообщением — например, может от него отказаться.
AllexIn
15.01.2016 12:51+2Получив сообщение через вызов метода объект может делать с ним все что захочет. Например, не обрабатывать.
Пожалуйста, укажите мне источник вашего мнения относительно сообщения применительно к ООП.
Я вот тут уж начал сомневаться, полез на википедию и там тоже не нашел ничего, что ставило бы к сообщениям в ООП обозначенные вами и iCpu требования.iCpu
15.01.2016 12:55+1А как получить отправителя?
не нашел ничего, что ставило бы к сообщениям в ООП обозначенные вами и iCpu требования.creker
15.01.2016 13:01+2Возьмем ObjC, канонический ООП язык. Отправителя автоматически не получить, броадкаст сообщений нет. Даже посылка сообщений максимально зарезана в пользу статического анализа, что выглядит уже как методы обычные.
А что до С++, передавайте отправителя аргументом.
Googolplex
15.01.2016 13:00+2en.wikipedia.org/wiki/Object-oriented_programming#Dynamic_dispatch.2Fmessage_passing
By definition, it is the responsibility of the object, not the external code, to 'on-demand' select the procedural code to run/execute in response to a method call, typically by looking up the method at run time in a table associated with the object.
В данном случае вызов метода ничем не отличается от вызова обычной функции, потому что метод, по сути, функция и есть. Понимаете, то, что управление передано в метод, уже означает, условно говоря, что объект «получил» «сообщение». Посмотрите, как ООП реализовано в Smalltalk, например — это именно то ООП, которое изначально подразумевалось под этим термином.
Однако, на самом деле, это действительно просто вопрос терминологии. То, что под терминами подразумевается, постоянно меняется, и то, что C++ и Java на протяжении всего их времени жизни называли ООП-языками, сместило/расширило суть понятия ООП. Поэтому называть ООП-языками C++, Java и прочие, на мой взгляд, вполне приемлемо. Например, та же штука произошла, хоть и более быстро, с термином RESTful. Что только под ним сейчас ни понимают, но в 99% случаев это даже близко не напоминает то, что изначально подразумевалось в исходной работе, где этот термин был введён.
Собственно, я только против того, чтобы вызов метода называли отправкой сообщения, потому что по сути это не так.
Chaos_Optima
15.01.2016 12:52+6Эм… впервые слышу что сигналы слоты являются обязательной атрибутикой ООП. Всегда считал что язык поддерживает ООП если в нём есть полиморфизм, наследование и инкапсуляция.
Googolplex
15.01.2016 13:04+1Эти критерии не тождественны объектно-ориентированности. Например, в Go есть всё, что вы указали (окей, наследование с натяжкой), но он не является объектно-ориентированным. Когда в Rust добавят простое наследование структур (такие RFC уже есть), он тоже будет содержать все эти концепции, но он также не будет ОО.
bromzh
15.01.2016 13:18+3Да что там go, по таким критериям Haskell — один из самый Ъ ООП языков, там даже полиформизм круче!
creker
15.01.2016 13:48+2Опять эта тема. Является он объектно-ориентированным, потому что покрывает все его принципы. Это тоже самое, что smalltalk и С++. Последний не обязан быть SmallTalk, чтобы быть ООП языком. Тоже самое с Go — это другой язык, но инкапсуляцию, наследование и полиморфизм своими фичами он вполне покрывает. Именно в этом определение ООП языков и не надо тут выдумывать науку из этого целую.
Googolplex
15.01.2016 14:39+3Нет, определение ООП-языков не в этом.
Object-oriented programming (OOP) is a programming paradigm based on the concept of «objects», which are data structures that contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods.
Объектно-ориентированный язык так или иначе поощряет проектирование и программирование в терминах объектов и взаимодействия между ними. Если это не так, то язык не является объектно-ориентированным. Наличие полиморфизма (кстати, какого именно? Параметрического, ad-hoc, подтипов?), наследования и инкапсуляции не является ни необходимым, ни достаточным условием. Например, в Python инкапсуляция отсутствует в принципе — вы всегда можете добраться до чего угодно в любом объекте, но он от этого не становится менее объектно-ориентированным. И наоборот, ни Go, ни Rust не поощряют проектирование в ООП-терминах (объекты, классы, иерархии и прочее), поэтому они не являются ООП-языками.creker
15.01.2016 14:52+1Еще как поощрается, только в своих терминах, которые тем не менее покрывают принципы ООП. Опять начинаются эти придирки к словам вместо того, чтобы смотреть на суть вещей. В Go есть объекты, есть интерфейсы, есть иерархии (да, немного не такие, к каким мы привыкли, те менее есть). Поощрается взаимодействие между ними. Более того, на нем никак иначе и писать невозможно.
Что до раста, то беглый просмотр в гугле приводит к выводу, что Rust мультипарадигменный язык, в том числе и объектно-ориентированный. Это просто не копия какого-то другого языка.saboteur_kiev
15.01.2016 15:29+3Основной принцип ООП — это то, что мы работем не с функциями и данными, а с данными, заключенными в объект ВМЕСТЕ с методами, которые эти данные обрабатывают.
Таким образом объект представляет собой самодостаточный код, в который может залезть программист, и фиксить, а не бегать по 100500 тысяч файлов в поисках всех связанных сущностей.
Все остальные штуки — наследования, полиморфизмы — это попытки сделать объекты более удобными, но основная суть — выше.bromzh
15.01.2016 18:38+1Основной принцип ООП — это то, что мы работем не с функциями и данными, а с данными, заключенными в объект ВМЕСТЕ с методами
Ну тогда ООП в лиспе и не ООП совсем.
Там мы работаем с функциями, только обобщёнными. И отдельно от них определяются данные — с помощью классов.
Оттого там, например, и диспетчеризация может быть более чем по 1-му параметру.
creker
15.01.2016 21:26+1Основной принцип ООП — это то, что мы работем не с функциями и данными, а с данными, заключенными в объект ВМЕСТЕ с методами, которые эти данные обрабатывают.
Ну тогда все еще проще. Go прям православный ООП язык.
bromzh
15.01.2016 22:47+1Например, в Python инкапсуляция отсутствует в принципе
Её «можно» организовать с помощью замыканий. Ну т.е. получить значение станет немного труднее. Потому что, видимо, в любом языке так или иначе можно-таки добраться до private- или спрятанных в замыкание значений.
MacIn
16.01.2016 15:54+1Нет, определение ООП-языков не в этом.
В цитате — как раз об инкапсуляции. GO через расширение структур это делает.
Если обратиться за определением к википедии, мы обнаружим 2 понимания термина «инкапсуляция»:
In programming languages, encapsulation is used to refer to one of two related but distinct notions, and sometimes to the combination[1][2] thereof:
A language mechanism for restricting access to some of the object's components.[3][4]
A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.[5][6]
В русскоязычной литературе и ВУЗах принято второе, выделенное жирным.
gurinderu
15.01.2016 13:44+1Я где-то ниже дал ссылку на ooc. Почитайте.
Никто вам не мешает писать на C в oo-style.
А еще лучше сразу гляньте GTK. Там все объектное, хотя чистый C.
R10
17.01.2016 17:27+1Программирование без ООП, даже больше — почему с ООП нельзя достичь high performance: www.dataorienteddesign.com/dodmain
bromzh
15.01.2016 12:08+2А разве из динамической либы, собранной из C++ можно дёргать классы через ffi? Там вроде как проблема была с виртуальными методами. В команде KDE специально писали libsmoke, чтобы можно было легко делать биндинги для других языков. В случае C всё было бы проще: дёргай ffi и всё.
Или сейчас уже нет проблем с динамическими либами на c++? А то я давно уже не писал на с++.
dordzhiev
15.01.2016 23:04+1Так зачем использовать зоопарк языков, когда все можно писать на одном.
Развивая вашу мысль, и POSIX, и WinAPI также имеют C-интерфейс снаружи. Теперь все приложения на C писать? Нет, спасибо, я уже даже без буста не могу, не говоря уже о том чтобы к vanilla С вернуться.
saboteur_kiev
15.01.2016 12:10+1Например, на хороший ИИ?
Когда-то давным давно, в Сталкере обещали живой мир, подразумевая, что пока игрок бродит в одной локации, в другой локации другие сталкеры бродят, дерутся с животными, популяция животных строго считается, в общем весь мир живет, независимо от того, в этой зоне игрок или нет. Но думаю, проблемы с производительностью не дали возможность это реализовать.
в крупных MMO, на сервер тоже может падать нагрузка, просчитывать поведение сотен тысяч MOBов.k12th
15.01.2016 12:21+1Ну если только. Обычно все-таки задачу «предоставить игроку сильного интересного противника» предоставляют мультиплееру, а не пишут мегасложный ИИ для босса.
saboteur_kiev
15.01.2016 12:44+1А причем тут мегабосс?
Обслуживание активного живого мира — сложная задача, поэтому большинство на нее забивает.
Это же вторая часть 3D, в котором не только «можно ли отсюда увидеть объект», но и «как отсюда пройти к нему, не застряв в текстурах», повторить для пары сотен объектов. Обычно делают уже готовые предрасчитанные пути. Из-за чего кардинальное изменение мира — не везде работает. Майнкрафт обошелся крупными кубиками и простейшим ИИ, а мог бы…
Nanako
15.01.2016 01:15+4Немного странные рассуждения. Я, например, пишу в основном на C#, на C++ и C периодически, и портирую кучу всего из одного в другой. Дак вот для меня дико сложный C, нормальный C++ и совсем легкий C#. Ну в смысле, работы с сокетами и системой, многопоточностью, IPC и все такое. И код читать проще, и вообще все как-то тупее в реализации получается. Производительность наверное да, страдает. Но не так чтобы ужас ужас.
PapaBubaDiop
15.01.2016 01:58+10Крик души понятен.
Все родное, удобное и вдобавок быстро компилируется.
Замечу, что Турбо-Паскаль в 5 раз быстрее компилировался, чем Турбо-С.
Да.
Но всё равно его пришлось бросить.
bromzh
15.01.2016 05:02+3Почему-то не упомянут Lua. А ведь можно взять SDL и lua-jit, в котором есть FFI. Весь код можно писать на lua, дёргая нужное из SDL через этот FFI. Ну и ещё всякие массивы объявлять как c-переменные (а то таблицы луа скоростью не блещут).
По скорости код будет немного уступать решению на С, зато (имхо) писать приятнее…justaguest
15.01.2016 06:45+1Ну тогда лучше сразу Haskell взять — богатая система типов, краткость, удобство… Но автор уже выкинул Go — потому что сборщик мусора.
bromzh
15.01.2016 07:11+2Луа — небольшой простой язык, его интерпретатор довольно маленький, встраивается куда угодно и его можно уместить в APK.
Не знаю про хаскель, но разве там не требуется слинковать все хаскелевские модули статически, чтобы получить standalone-приложение? И соберётся ли всё это под нужную архитектуру? Скомпилить и запустить хаскелевскую программу можно, но судя по ответам на SO — это тот ещё гемморой.
А ещё плюсом подхода с луа является ещё то, что изменения возможно будет вносить без переустановки/перекомпиляции всего, достаточно на девайсе изменить скрипт. Скорость разработки увеличивается, отладка упрощается.justaguest
15.01.2016 07:42+1Стоп, мы ведь говорим о платформах с сишными библиотеками, как SDL? Да, соберется. И да, компиляция статическая, т.к. происходит проверка типов по всем модулям. Ну, а Lua не статически типизирован, и хотя, наверное, изредка это удобно — например, как вы выразились, подправить что-то на конечном дейвасе — в разработке от этого больше проблем. Чем больше проект, тем сложнее писать на динамическом языке, именно потому что все проблемы всплывают на конечном девайсе, куда это все еще надо загрузить и запустить ;)
Как пример таких проблем могу вспомнить питоновский PyUNO — объективно, это не вина языка, просто это самое отвратительное АПИ какое я когда-либо встречал. Штука в том, что половина его проблем исчезла бы, если бы АПИ было на статическом языке.bromzh
15.01.2016 08:03+1И да, компиляция статическая, т.к. происходит проверка типов по всем модулям.
Я имел ввиду линковку. Т.е. если я использую какой-то модуль, мне его нужно слинковать статически с итоговым бинарником. Не выйдет ли так, что в итоге будет тяжёлый бинарник с большим рантаймом?
На счёт того, что лучше, не буду спорить, всё равно каждый останется при своём мнении. Я лишь высказал свои идеи, которые меня как-то посетили.
bromzh
15.01.2016 06:55+1Кстати, ведь SDL портирован под кучу платформ. LuaJIT написана на сях и тоже работает почти везде. И это навело меня как-то на безумную мысль, что на такой связке можно писать простенькие и при этом довольно шустрые игры почти для всех популярных платформ (кроме веба).
Ведь чтобы запустить программу, написанную вышеизложенным способом, достаточно запустить в интерпретаторе луа наш скрипт. На десктопных ОС всё обирается и запускается легко. На мобильных платформах провернуть всё это труднее, но вполне возможно: взять заранее собранные для нужной платформы динамическую либу SDL и интерпретатор луа, взять скрипты с другими ресурсами (звук, видео, картинки), написать простенькую запускалку всего этого и запаковать всё.
Получится что-то типа LOVE (кстати, он теперь использует LuaJIT по-умолчанию), но проще.
AllexIn
15.01.2016 07:50-1Да, С++ сложнее С.
Он настолько сложен, что ни один(или все такие есть такой?) компилятор не поддерживает его стандарт полностью.
Например, я пишу на С++ игры, но я некоторые возможности С++ не знаю и не изучаю. Например, я почти не ориентируюсь в шаблонах и без документации не смогу написать даже простого. Просто потому, что шаблоны практически не применяются при программировании игр.
ООП — это не серебряная пуля. Но, если код пишет человек без ООП головного мозга, то код получается нагляднее и чище, чем на plain C. Я уверен, что при прочих равных код игры на С++ будет проще и нагляднее.
Мне непонятен наезд на С++ про скорость компиляции. Современные процессоры и инкрементальная компиляция сделали компиляцию на С++ достаточно быстрой. Если же этого не хватает, то можно поместить исходники проекта на RAM диск и компиляция станет практически мгновенной.
ИМХО автор пишет огромные портянки в каждом CPP, пихает реализацию в H и в итоге, конечно, в медленной компиляции виноват С++.
P.S.
Я то вообще Delphi люблю. Но С++ стал стандартом игростроения не просто так.slonopotamus
15.01.2016 09:56+8C++ отвратительно медленно компилируется, вы легко можете в этом убедиться попробовав скомпилировать любой крупный C++-проект (OpenOffice, Chromium, UE4, kdelibs). Потому что в процессе происходит дублирование тонны информации в .obj-файлах. Вполне типична ситуация, когда .obj-файлы суммарно занимают, скажем, x100 места по сравнению с результирующим бинарником.
alexeibs
15.01.2016 10:17+2Ну так каков размер исходников Chromium'а? Клонирование всего дерева репозиториев запросто может быть медленнее, чем непосредственно сборка.
SHVV
15.01.2016 10:34+4Ну например у нас тоже большой проект.
Экспорт кода из репозитория занимает несколько минут, а сборка всего проекта половину дня.
И при этом 8-ми ядерный i7 (4 реальных + 4 виртуальных) загружен на 100%.
То есть даже не в диск упирается.
AllexIn
15.01.2016 11:10+2Когда мне нужен ребилд — я просто ставлю его на ночь. А в процессе работы инкрементальная сборка сводит компиляцию к секундам.
slonopotamus
15.01.2016 11:14+10Когда мне нужен ребилд — я просто ставлю его на ночь.
Ну в общем-то предлагаю на этом закончить, вы и сами признали, что C++ медленно компилируется.AllexIn
15.01.2016 11:16+3Не надо демагогии.
Ребилд не является основной операцией в процессе разработки и скорость его не имеет принципиального значения.slonopotamus
15.01.2016 11:21+8Я хочу чтобы проект билдился в пределах единиц минут вне зависимости от того, какой файл понадобилось поменять. В противном случае, могут возникать всякие нездоровые мысли типа «не буду рефакторить этот кривой метод, потому что придётся поменять сигнатуру в H-файле и получить пересборку на два часа».
AllexIn
15.01.2016 11:31+2Давайте меньште абстрактных «два часа» и больше конкретики:
store.steampowered.com/app/380770 сессионная ММО на С++
Исходники одного только движка 16 мегабайт. Плюс еще столько же сама игра. Ребилд в 2015 году — около 10 минут.
store.steampowered.com/app/11560 РТС c терраформингом
Я не помню сколько там весили исходники. Использовался IncrediBuild. В 2006 году полный ребилд занимал меньше 30 минут.
Уж простите, но игры автора даже при полной пересборке всего врядли больше минуты компилятся.
SHVV
15.01.2016 11:38+5Когда мне нужен ребилд — я просто ставлю его на ночь.
А утром прийти и увидеть, что за ночь ничего не скомпилировалось, потому что таки допустил ошибку в коде. И всё, ещё один день впустую!
P.S. Я тут сижу просто потому, что у меня всё ещё компилируется. ;-)AllexIn
15.01.2016 11:50+3Ошибку в интерфейса допустить сложнее чем в реализации. Да и компиляция прервется в самом начале, ведь если мы меняли хидер, то именно он будет скомпилирован первым и только потом «тысячи» файлов зависимостей от него.
Ну а изменение кода в CPP не приведет к перекомпиляции проекта.
Так что не принимается.SHVV
15.01.2016 13:43+1Да, обычно так и бывает, но не всегда.
Например во время рефакторинга, когда затрагивается несколько библиотек. К счастью, это редкость.
В любом случае, это не отменяет того факта, что компилятор С++ один из самых медленных по сравнению с другими популярными языками.
0xd34df00d
15.01.2016 15:11+1Вероятнее всего, как уже ниже написано, либо сразу увидите, не успев встать из-за машины, либо проблема в паре .cpp-файлов, и тогда
make -k
вас спасёт.
saboteur_kiev
16.01.2016 03:38+1У вас просто маленький проект.
Мы пишем ОС, и компиляция с нуля идет около 8 часов на хорошем рабочем компе.
При помощи всяких ухищрений, icecc, sstate и др, добились около 2-3 часов.
Даже до 25 минут на компе с 32 ядрами и 128 гб оперативки.
Но до секунд не добраться никак. 80% времени — компиляция, 20% — дисковая подсистема.AllexIn
16.01.2016 10:37+2Во-первых — при чем тут ОС? Речь о разработке игр.
Во-вторых я уже приводил пару примеров проектов в которых работал программистом, я бы не назвал их маленькими. :)
В-третьих — имеет смысл анализировать не ваши или мои проекты, а проекта автора.
slonopotamus
15.01.2016 10:03+3пихает реализацию в H
C++ заставляет пихать реализацию темплейтов в H. И заставляет пихать объявления private-методов. При чем здесь автор, это язык такой.AllexIn
15.01.2016 11:12И много шаблонов вам надо в геймдеве?
Я работал над несколькими действительно большими проектами и шаблоны встречал только на самом низком уровне движка. Они были раз реализованы и никогда не менялись.
Отдельно интересно, как автор решает проблему шаблонов в хидере в своих играх на plain C.Chaos_Optima
15.01.2016 11:22+3Отдельно интересно, как автор решает проблему шаблонов в хидере в своих играх на plain C.
Наверно никак, потому что в С нет шаблонов.AllexIn
15.01.2016 11:23+4О чем и речь.
Отказываться от С++ потому что он медленно билдит шаблоны в пользу С на котором шаблонов нет вообще…Chaos_Optima
15.01.2016 11:29+5Я лично тоже не вижу причин отказываться от С++ в пользу С, одних деструкторов достаточно чтобы забыть про С и влюбится в С++.
slonopotamus
15.01.2016 11:29Отдельно интересно, как автор решает проблему шаблонов в хидере в своих играх на plain C.
В plain C нет проблемы шаблонов. Ну и вы как-то ловко проигнорировали вторую часть, про объявления private-методов в заголовках, проблемы с которыми в plain C кстати тоже нет.AllexIn
15.01.2016 11:32+5Намека на шаблоны не достаточно?
Ладно, я намекну и на private: и как же автор решает проблему объявления приватных методов в plain C? :)gwer
15.01.2016 11:38+1Не надо ООП тащить в pure vanilla C, и проблем не будет. Функция изначально приватная, если не сказано об обратном (extern).
bromzh
15.01.2016 11:41+1Может как в питоне, т.е. никак? Ответственность с разраба библиотеки переложить на плечи программиста, который её использует. Можно договориться о наименовании и вызывать «скрытые» методы только на свой страх и риск.
Так-то, private — тот ещё костыль, т.к. к нему при должном умении всё равно можно достучаться.
MacIn
16.01.2016 16:04-2Если же этого не хватает, то можно поместить исходники проекта на RAM диск и компиляция станет практически мгновенной.
Ох ты ж… последний раз приходилось применять этот трюк, когда работал в 90х на 286 процессоре с 4 Мб памяти и 40 мегабайтным MFM диском. Компилятор Fortran а производства MS был дико медленен по сравнению с другими платформами. Только RAM диск и спасал.
А гляди ж ты — опять такой трюк работать будет.
Мда, как же здорово, что у нас проекты на Delphi с полным ребилдом собираются за 15-20 секунд. Миллионы строк.AllexIn
16.01.2016 16:09+2Я RAM диск использовал для С++ проектов лет 6 назад последний раз.
С тех пор я открыл для себя инкрементальную компиляцию и перестал извращаться с RAM дисками.
vladon
18.01.2016 15:04+4Да, С++ сложнее С.
Как по мне, так наоборот.
Он настолько сложен, что ни один(или все такие есть такой?) компилятор не поддерживает его стандарт полностью.
В clang полностью реализован весь стандарт C++14. В gcc, вроде бы, тоже, не слежу за ним. И даже MSVC почти догнал.
А вот как раз C11 не реализован нигде целиком.
Например, я пишу на С++ игры, но я некоторые возможности С++ не знаю и не изучаю. Например, я почти не ориентируюсь в шаблонах и без документации не смогу написать даже простого. Просто потому, что шаблоны практически не применяются при программировании игр.
Шаблоны относятся к программированию игр ортогонально. Одно не зависит от другого. Это именно вы их не применяете безотносительно области вашей разработки. Если бы вы разрабатывали что-либо другое, то точно так же не применяли бы их. Впрочем, никто же и не заставляет, в этом и прелесть.
А насчёт скорости: в C++17 появятся модули, и тогда по скорости компилирования C++ догонит Паскаль (Дельфи).AllexIn
18.01.2016 15:12+1Это именно вы их не применяете безотносительно области вашей разработки.
Вы пропустили ту часть, где я говорил об опыте чтения кода больших игр, а не написании. При чем тут мой код, если речь не о моем коде?
Если бы вы разрабатывали что-либо другое, то точно так же не применяли бы их.
Опять ошибочная догадка. К примеру, писал Тулкиты и активно в них использовал шаблоны, например, для property.
В игра на высоком уровне шаблоны просто не нужны.0xd34df00d
18.01.2016 15:19+2В игра на высоком уровне шаблоны просто не нужны.
Почему? Например, легко допустить возможность ситуаций, когда возможно рантайм-полиморфизм (виртуальные функции, это всё) заменить на статический полиморфизм (boost.variant, это всё). Профитом получаем повышенную типобезопасность и быстродействие.
vladon
18.01.2016 15:20+1Шаблоны — это просто техника программирования, обобщённые алгоритмы.
Я даже навскидку сейчас придумаю с ходу, зачем могут понадобиться: есть N типов спрайтов и есть единый алгоритм для их обработки, например.
А почему не используются, тоже можно объяснить: обычно в таких проектах есть некий гуру-старпёр, который их просто не понимает и не хочет понимать. Однажды на одном из собеседований я не прошёл, потому что использовал лямбды, цитата: «незачем использовать эту новомодную лапшу из новых стандартов, и вообще новые стандарты не нужны».AllexIn
18.01.2016 15:21+1Пример слишком абстрактный. Давайте конкретнее.
vladon
18.01.2016 15:27+1Например, алгоритм наложения одного спрайта на другой не зависит от сути этих спрайтов.
В такой алгоритм логично передавать типы спрайтов.AllexIn
18.01.2016 15:30+1Повторюсь: слишком абстрактный пример. Не понятно, в чем преимущества по сравнению с обычным наследованием спрайтов от общего предка у которого прописан метод для этого абстрактного наложения.
Речь же не о том, что нельзя шаблонами писать, речь о том что это не нужно как правило.
Ваш пример он как раз об этом: можно, но не понятно зачем.vladon
18.01.2016 15:33+1Так это вы просто не понимаете, с этого и надо было начинать :-)
Это просто два способа решить одну и ту же задачу. Каждый выбирает себе по вкусу. Вижу минусы и плюсы и у того и у другого варианта.AllexIn
18.01.2016 15:48+1Конечно я этого не понимаю. Если бы я понимал преимущества шаблонов в конкретной этой ситуации, я бы не говорил что они не нужны.
По всему получается, что вы эти преимущества понимаете и сможете объяснить.vladon
18.01.2016 15:56+3Самые очевидные плюсы шаблонов: у вас реально один код, причём вы можете его специализировать (т.е. делать отличающуюся реализацию) для конкретных типов. Или даже для определённого порядка типов.
Например, с теми же спрайтами. Есть общий класс Sprite. Есть наследуемые, например: Dragon, Knight и Princess (: public Sprite).
Наложение для всех сочетаний одинаковое. Реализуйте общий алгоритм наложения своим способом (ну, можно это сделать методом в Sprite).
А теперь специализируйте это наложение именно для наложения Knight на Dragon, допустим, а в остальном оставьте то же самое.AllexIn
18.01.2016 15:58+1То есть для того чтобы сделать уникальную обработку для Knight/Dragon мы должны менять код в базовом шаблоне? Или я не правильно понял?
vladon
18.01.2016 16:02+4Как раз нет. Можно либо сделать template overloading, либо template specialization. Гуглите.
Если без шаблонов вы это будете специализировать, скорее всего, в конкретном классе (оверрайдом виртуальной функции), т.е. код может быть разбросан по куче разных файлов, то при использовании шаблонов всё будет рядышком. Красиво, чисто, удобно, а самое главное — нет грязи (возможных проблем) с указателями. И можно не делать виртуальную функцию (что накладывает, конечно же, свою печать на производительность).
Nanako
18.01.2016 16:12+1По моему пример не очень. Тут лучше коллекции подойдут. Например у вас есть классы наследованные от «спрайта», есть классы наследованные от «звука», есть классы наследованные от «ресурса». И вам нужен мультиплеер или для ИИ нужно контролировать какие звуки и спрайты кто имеет чтобы рассчитать кто кого видит/слышит или передачу чего-то по сети, например. И вместо того чтобы устраивать множественное наследование всего от одного класса или городить интерфейсы вы делаете темплейт обертку/коллекцию которая помечает что кому принадлежит и может это все индексировать и хранить с хэшем. И потом создаете для звуков Обертка<Звук>, для спрайтов Обертка<Спрайт> в коде, и у вас одна имплементация «контроля принадлежности» вне зависимости от того объекты какого класса вы решили контролировать.
vladon
18.01.2016 16:14+1Я пытался упростить уж. И показать, что даже на примере одного вида объектов (тупо спрайтов) шаблоны очень помогают. Чего уж говорить о взаимодействии кучи разных иерархий.
Nanako
18.01.2016 16:18+1Ну шаблоны в простых ситуациях слабо помогают, зато сегодня у меня вот такой кадавр родился:
public sealed class MarketDataStream<T,X,Y> : IDisposable, ILoggerAttached, INamed where X: IStreamHeader where Y: IStreamFeedbackHeader
vladon
18.01.2016 16:20+1Это же не C++.
Nanako
18.01.2016 16:32+1А какая разница, я могу ошибаться, но можно так:
template <class T, IStreamHeader X, IStreamFeedbackHeader Y> class MarketDataStream : public IDisposable, public ILoggerAttached, public INamed { }
Я не так хорошо знаю С++ правда, если есть ошибки, скажите, мне самому интересно0xd34df00d
18.01.2016 16:39+1Подойдёт только в том случае, если IStreamHeader или IStreamFeedbackHeader — типы вроде enum. Требование к наследованию от интерфейса (что, насколько я знаю, представляет where в C#-коде) так выражать не получится, для этого нужны либо концепты из C++17, либо всякие std::enable_if + std::is_base_of.
Nanako
18.01.2016 16:46+1Ясно, это я еще помню как override в С++ раньше не было, тоже удивлен был.
IStreamHeader и IStreamFeedbackHeader интерфейсы struct, т.е. в С++ тоже не получится?vladon
18.01.2016 16:50+1В C++ struct и class — одно и то же, нюансы в видимости по дефолту.
Я так понял, вам нужно, чтобы был передан объект, который реализует определённый интерфейс?
Два варианта: как сказал выше 0xd34df00d, либо просто передавать их в конструктор (а смысл шаблониться от конкретных объектов? Эдак у вас на каждый объект будет разворачиваться шаблон, отсюда и требование.)Nanako
18.01.2016 16:56+1Ну там чуть посложнее, там передается поток данных, а в начале memorymap два заголовка, чтобы не локать треды. Передатчик и приемник знают какие структуры они пишут, а обертке потока нужно только пара параметров которые есть во всех заголовках. В итоге все структуры заголовков унаследованы от
public interface IStreamHeader { Guid Tag { get; set; } long Size { get; set; } long Heartbeat { get; set; } int DataOffset { get; set; } bool Choked { get; set; } }
Размер структуры в заголовке чтобы считать смещение следующей, ну и т.д. В итоге указание интерфейса структур в шаблоне это просто страховка, можно и не указывать на самом деле.vladon
18.01.2016 16:59+1Так в C++ делайте в конструкторе, помимо того что невозможно — ещё и незачем указывать их как параметры шаблона.
vladon
18.01.2016 16:52+1Т.е. вам надо:
template <class T> class MarketDataStream : public IDisposable, public ILoggerAttached, public INamed { // конструктор MarketDataStream(IStreamHeader X, IStreamFeedbackHeader Y) { // ... } }
vladon
18.01.2016 16:53+1Ясно, это я еще помню как override в С++ раньше не было, тоже удивлен был.
оверрайдить-то всегда можно было, просто не было явного ключевого слова.Nanako
18.01.2016 17:07+1Ну в C# и Java указывать override это mandatory. Я в смысле что ООП в С++ оно вольное какое-то. Я до сих пор в восторге от Питона, правда не пишу на нем сейчас практически.
vladon
18.01.2016 17:09+1Ну не сравнивайте язык, в котором ООП был спроектирован изначально (C#) гением (создателем Delphi) и где, в принципе, иногда забивают на обратную совместимость (это правильно, кстати), с языком, в котором вынуждены обеспечивать обратную совместимость из-за тех, кого я называю гуру-старпёрами.
0xd34df00d
18.01.2016 15:22+5Однажды на одном из собеседований я не прошёл, потому что использовал лямбды, цитата: «незачем использовать эту новомодную лапшу из новых стандартов, и вообще новые стандарты не нужны».
Я в таких случаях всегда радуюсь, что наша взаимная несовместимость с потенциальной командой открылась так рано.
0xd34df00d
18.01.2016 15:21+1В gcc, вроде бы, тоже, не слежу за ним.
Формально реализован, по факту встречаются баги сильно чаще, чем в clang. Причём, чаще это мешающие баги — clang скорее акцептит невалидный код, тогда как gcc реджектит валидный.
А насчёт скорости: в C++17 появятся модули
Как у них там с экспортом темплейтов из модулей, кстати?vladon
18.01.2016 15:24+1Формально реализован, по факту встречаются баги сильно чаще, чем в clang. Причём, чаще это мешающие баги — clang скорее акцептит невалидный код, тогда как gcc реджектит валидный.
Не знаю, мне gcc не нравится по скорости компиляции, да и в clang аналитики больше, те же санитайзеры чего стоят. Кстати, есть пример акцепта clang'ом невалидного кода?
Как у них там с экспортом темплейтов из модулей, кстати?
Я не слежу, в clang пока не появилось.0xd34df00d
18.01.2016 15:28+1Не знаю, мне gcc не нравится по скорости компиляции, да и в clang аналитики больше, те же санитайзеры чего стоят.
Аналогично.
Кстати, есть пример акцепта clang'ом невалидного кода?
Например. Кстати, блин, так и не обновил.
Я не слежу, в clang пока не появилось.
Я какой-то то ли пропозал, то ли обсуждение читал, и там как-то всё плохо с темплейтами.vladon
18.01.2016 15:58+1Я какой-то то ли пропозал, то ли обсуждение читал, и там как-то всё плохо с темплейтами.
Фиг его знает, что будет, но одним из недостатков include'ов авторы разных пропозалов о модулях заявляют как раз неудобство реализации шаблонов в инклюдах. Вчитываться вообще нет времени…
SHVV
15.01.2016 08:56+6Кармак тоже раньше писал свои движки на C. Но начиная с третьего Дума таки перешёл на С++. Итересно почему?
jia3ep
15.01.2016 10:20+3Может быть потому, что для написания третьего Дума пришлось набирать много новых разработчиков, а программистов на C искать все сложнее с каждым годом.
Хорошо еще на C++, а то сейчас в резюме чаще указывается знание неизвестного мне языка C/C++, а вот знание C или C++ теперь встречается все реже.AllexIn
15.01.2016 11:13+5Сфера применения знака / (косая черта) — научная и деловая речь. Он употребляется в следующих функциях.
1. В функции, близкой к союзам и и или, как знак альтернативности понятий или обозначения единого сложного понятия.jia3ep
15.01.2016 11:32+5Это неприменимо при указании названия языков. Например, язык C++/CLI является отдельным языком и имеет свой стандарт — ECMA-372.
И вообще, это была шутка на тему того, что множество людей, которые указывают в резюме C/C++, не тождественно множеству людей, которые знают C и C++ одновременно.AllexIn
15.01.2016 11:35+1Это неприменимо при указании названия языков.
Почему? Я бы с вами согласился, если бы был язык C/C++, но него нет. Так что нет противопоказаний для такого употребления.xaizek
15.01.2016 12:34+2Согласно Вашему же комментарию выше, C/C++ следует трактовать одним из следующих способов:
- C и C++
- C или C++
Т.е. приходим к выводу, что применимость обозначения «C/C++» настолько узкая, что оно практически всегда применяется неправильно. А если ещё и учесть двоякость трактовки («и» или «или»), то смысл использования такой записи вообще теряется, так как не понятно, что хотели сказать.AllexIn
15.01.2016 12:36+2Я С/C++ всегда воспринимаю так: я пишу на С++ и знаю чем он отличается от С.
0xd34df00d
15.01.2016 15:12А мне и язык C++ неизвестен. Надо указывать точно C++03 либо 11 либо 14!
jia3ep
15.01.2016 15:29+1Язык все-таки C++. А вы говорите уже про разные версии стандарта. Например, ISO/IEC 14882:2011(E) — это стандарт языка C++, который определяет, что «C++ is a general purpose programming language». Тоже самое написано и в других версиях стандарта.
0xd34df00d
15.01.2016 16:41+4Однако, идиомы и подходы к написанию кода на C++03 и C++11 существенно отличаются. После изучения C++03 необходимо потратить значительное количество времени, чтобы начать писать на «правильном» C++11. Знание C++03 не означает знание C++11.
Да и с практической точки зрения, действительно, если я беру человека в проект, то мне надо знать, C++ какой именно версии он знает. Между 03 и 11 разница таки побольше, чем между 98 и 03, где это было несущественно.jia3ep
15.01.2016 19:05+1С тем, что лучше указывать свой опыт работы с конкретной версий языка, согласен. Тут даже можно добавить с какой версией предпочитает работать человек, чтобы потом не было взаимных обид и разочарований.
sborisov
15.01.2016 11:14+9www.linux.org.ru/forum/talks/8720321
Как сообщает похороникс, Джон Кармак высказал своё окончательное мнение о противостоянии C и C++. Для обделённых знанием английского или пониманием божественности плюсов перевожу:
Я до сих пор считаю код Quake 3 более чистым — в известном смысле. Это венец эволюции моего стиля на C, и одновременно первая итерация моего стиля на C++. Но такое отношение может быть вызвано меньшим количеством строк или тем фактом, что я не особо-то и не заглядывал туда уже десять лет. Я думаю, что «хороший C++» лучше, чем «хороший C» с точки зрения читаемости, а всё остальное эквивалентно.
Я, конечно, поизвращался над C++ в Doom 3 — ведь я был опытным C-шным программистом с некоторым знанием ООП, пришедшим из Objective-C времён NeXT, так что я просто начал писать на C++ без нормального изучения юзкейзов и идиом. Вспоминая прошлое, я очень жалею, что не прочитал Effective C++ и некоторые другие книги. Часть остальной команды в прошлом имела опыт работы с C++, но они в целом следовали моим стилистическим решениям.
В течение многих лет я не доверял обобщённому программированию, и все ещё применяю шаблоны с опаской, но в итоге я решил, что удовольствие от статической типизации перевешивает нежелание иметь раздутый код в заголовках. В Id Software всё ещё идут споры об использовании STL, и со временем они становятся жарче. Если же вернуться снова к временам, когда началась разработка Doom 3, призыв использовать STL тогда стал бы неудачной затеей, но сегодня в пользу этого решения уже есть серьёзные аргументы, в том числе в геймдеве.
Также я теперь стал const-нацистом, и даю по рукам программисту, который не ставит const подходящим для этого переменным и параметрам.
Главным нововведением, интересным для меня, является функциональное программирование — оно позволяет избавиться от многих старых привычек и вновь отказаться от некоторых идей ООП.justaguest
15.01.2016 13:01+1Кстати по поводу const — любопытный факт, но далеко не всегда в ситуациях при оптимизации, где, казалось бы, компилятор должен догадаться о неизменяемости, он действительно догадывается. Помню, пол-года назад мне надо было оптимизировать прогу под встраиваемое устройство. Можно было переписать часть функций, превратив их в трудноотлаживаемую мешанину, но по опыту в будущем ни к чему хорошему это бы не привело. Поэтому — помимо игр с опциями оптимизаций компилятора — огромная часть проделанной работы оказалась простым путешествием по всем замешанным функциям кода, и заменой переменных и аргументов на const везде, где только можно. Работала такая оптимизация прежде всего со всякими векторами и структурами. Для проверки, сработало ли в очередной раз, достаточно перекомпилировать один объектный файл с оптимизациями — и, если размер уменьшился, следовательно компилятор смог применить еще какую-то оптимизацию.
burjui
15.01.2016 17:26+2Может, и так, но «const-нацизм» используется в первую очередь для того, чтобы оградить себя от случайной модификации данных (будь то аргументы или локальные переменные), и уже во вторую очередь — для оптимизации. D C++ это работает так себе, поэтому в D пошли ещё дальше, и сделали const транзитивным:
void fn(const int *x) { int y; x = &y; // error *x = y; // error }
Вот так в D объявляется изменяемый указатель на неизменяемый int:
const(int) *x
Если указатель константный, то и данные, доступные через него — тоже. Вот это настоящий const-нацизм.Chaos_Optima
15.01.2016 18:37+5В С++ это тоже есть const int* const x
burjui
15.01.2016 22:08+1Я в курсе, но в D const int* x по умолчанию не даёт выстрелить в ногу, а C++ вариант const int* const x выглядит уже не так опрятно. В C++ вообще много чего есть, но есть языки, в которых это сделано лучше.
mustitz
16.01.2016 00:12+1const и restrict дают очень много в плане оптимизации, потому что любая модификация памяти через указатель должна сбрасывать все закешированые ранее в регистрах значения. В C++ для этого есть свои достаточно специфичные правила (перечитываются только значения того типа, который изменился по указателю). Кто-то даже шутил, что использование restrict при реализации математических методов позволяет C по скорости догнать FORTRAN.
Если брать const как защиту от случайной модификации, то теоретически да. Но на практике я больше вспоминаю случаи, когда погорячился с const, и указатель надо было вернуть в неконстантное состояние, часто по длинной цепочке, иногда с пересмотром архитектуры. А вот когда бы это действительно помогло… не помню… Может быть, но уверенности нет.justaguest
16.01.2016 11:06+1Это как парадокс, что пользователи чаще пишут отзыв при плохом происшествии; когда же все идет отлично, они молчат — люди просто не обращают внимание, что все хорошо. Я могу вам назвать, когда const действительно полезен — прежде всего при отладке. Как только у вас что-то идет не так, а особенно в трудностях локализации ошибки — const в аргументах и теле позволяют сразу сказать: «Ага, я не модифицировал это значение, значит ошибка не тут!». В частности может помочь, когда переполнение буфера (да, я в курсе про address sanitizer, но предположим его у вас нет), и все выглядит, словно изменяется именно эта переменная.
vladon
18.01.2016 16:57+1Вот и Линус Торвальдс такой же, не понимает он C++ — и все вынуждены писать в ядре на Си.
sborisov
18.01.2016 16:57что такое ABI в курсе?
vladon
18.01.2016 16:59+1В курсе. Поэтому предпринимаются попытки его стандартизировать. Просто не было необходимости.
0xd34df00d
18.01.2016 17:08+2Ну и причём ABI к ядру, которое только относительно недавно стало собираться чем-то, отличным от gcc?
Кстати, я тут ядро собрал gcc 4.8, а модуль потом дособрал с gcc 4.9, чего-то модуль не загружается. Как же хвалёный стабильный C ABI?
maaGames
15.01.2016 10:02+4> ООП… почему надо так жёстко объединять код и данные.
Кхм… Я не понял связи между ООП и объединением кода с данными.
Посмотрел игры автора, половину из них можно было написать на JavaScript или вообще на HTML5. Только зря любимую сишку насиловал…
Раз такое пропустили на Хабр (и ведь даже не на Гиктаймс, а на Хабр!), может и мне стоит написать статью, как игру за три дня написал. Пусть мне уже и объяснили, что три дня это довольно много.)
AjnaGame
15.01.2016 10:19+40groaner
15.01.2016 11:39+1Самый обстоятельный, убедительный и отлично написанный разбор того, почему C в сто раз лучше чем C++ — C++ Frequently Questioned Answers. Вот бы ещё кто-нибудь совершил подвиг и на русский язык это перевёл.
0xd34df00d
15.01.2016 15:18+3Прочитал несколько первые ответов из Big Picture Issues — так можно про любой достаточно популярный язык написать. 6.1 для того же C применим простым удалением из текста всех плюсиков.
Ну и необоснованные утверждения — это супер.
«Templates are hard to use» — а я говорю, что нет, они простые.
Using C instead of C++ has several practical benefits: faster development cycle, reduced complexity, better support by tools such as debuggers
Ну это просто смешно. Тут я не выдержал и закрыл страничку, извините.
izvolov
15.01.2016 17:33+1Автор просто плохо знает язык.
Многие вещи субъективны, многие откровенно ошибочны, многие устарели в связи с выходом 11-го и 14-го стандартов или стремительно устаревают в связи с подходом 17-го.
groaner
15.01.2016 11:41-1Пункт насчёт скорости компиляции не совсем понятен — вряд ли C компилируется быстрее чем, например, C# или Java.
elanc
15.01.2016 12:24+1Кстати, раз упомянули разработчиков в области GameDev, творящих на Си: слежу за этим гражданином — www.twitch.tv/quel_solaar/profile
И уж поверьте — уровень графики в его текущем проекте просто превосходный!creker
15.01.2016 12:49+3Уровень графики зависит от самой графики — шейдеров и арта. Написан движок на С или C# уже имеет малое значение, потому что современные проекты упираются в GPU в подавляющем большинстве случаев. Взять тот же Unity на mono. Когда же тебе важна CPU производительность, то выбор практически всегда С++. Банально, настолько сложные архитектурно проекты сложно писать на С и С++ не случайно является де-факто стандартом в геймдеве.
potan
15.01.2016 18:49+8В качестве альтернатив Rust даже не упоменается. Автор про него не слышал?
Хотя Rust все-таки немного сложнее C, эта сложность оправдана — ни чего лишнего там нет.vladon
18.01.2016 15:05+1Я бы сказал, что он даже сложнее C++
potan
18.01.2016 15:36+3В C++ очень много неочевыдных подводных камней, типа невиртуальных деструкторов или изменение класса объекта в процессе работы конструкторов.
В Rust же таких неожиданностей нет. Да и сложных концепций, типа раследования.
Владение и заимствование — достаточно сложная и непривычная вещь, но она проще, чем «умные указатели».
Так что в целом Rust на порядок проще.
Ivan_83
16.01.2016 03:37Языков с каждым днём всё больше.
Некоторым нравится синтаксис, другим что решены какие то проблемы которые их лично очень беспокоили.
Тем временем только С остаётся языком живым, мало меняющимся и совместимым со всеми остальными языками и платформами.
Те я могу взять код которому лет 30 (почти как мне) и может быть с минимальными правками его собрать и запустить. (речь про алгоритмы не привязанные к железу/архитектуре)
И смогу тоже самое сделать ещё лет через 30.
Можно один раз в жизни написать какой то алгоритм и потом всю жизнь им пользоваться где угодно просто подключая 1-2 файла.
Уверен, мало кто тут пережил смерть своего любимого языка и осознал что всё что было написано за предыдущие годы теперь просто бесполезные файлы.
Автор прошёл это с флешем, я с вижалбейсиком и квикбейсиком.
Ещё один важный аспект в том, что код на чистом Си реально легко таскать между платформами, за исключением нюансов с BE/LE, но это мелочи в сравнении с тем что в других языках оно вообще практически никак.
Кто то считает огромным минусом что нужно самому рулить памятью, рулить типами и вообще всем.
Это большая свобода и большая ответственность, но уж лучше так, чем сражаться с языком когда доходишь до предела его возможностей и натыкаешься на невидимую стену из багов или ограничений производительности.
Лучше я сам буду рулить памятью чем сборщик мусора будет фризить мою прогу когда ему вздумается, и мне виднее когда память выделять а когда освобождать.
В случае Си мне всегда понятно что оно делает и как, никаких махинаций за ширмой, никакого волшебства, нюансы конечно есть но их довольно мало в сравнении с языками которые на себя берут заметно больше.
Не спорю, писать маленький проект с нуля на си не очень то и быстро/удобно, особенно где никакой число/байто грызки не требуется.
Но на средних проектах, уже может статься что можно заюзать массу кода который кто то написал, и не просесть в производительности на расчётах или в/в — всегда можно хоть до AVX/NEON дойти и выжать все соки из железа.
Большие проекты это всегда боль, при любом языке.
Ещё один не очевидный плюс обычного Си это что на нём написано ядры *бсд, линуха и куча дров и софта под них, и когда мне приспичивает то я лезу и патчу, потом этим можно поделится и добавить в резюме. А патчи к ядру или ещё чему то всем известному смотрятся круче, чем какой то там неведомой штуке на новом крутом языке :)creker
16.01.2016 17:53+4Это большая свобода и большая ответственность, но уж лучше так, чем сражаться с языком когда доходишь до предела его возможностей и натыкаешься на невидимую стену из багов или ограничений производительности.
Лучше я сам буду рулить памятью чем сборщик мусора будет фризить мою прогу когда ему вздумается, и мне виднее когда память выделять а когда освобождать.
В случае Си мне всегда понятно что оно делает и как, никаких махинаций за ширмой, никакого волшебства, нюансы конечно есть но их довольно мало в сравнении с языками которые на себя берут заметно больше.
Ох уж эти контрол фрики. Подобные рассуждения обычно свойственны, простите уж, мало опытным программистам. Они вечно хотят все контролировать, все делать сами, знать обо всем на свете. Я помню по себе, когда в ObjC с ручного управления памяти перешли на автоматический подсчет ссылок. И аргументы для самого себя у меня были теже самые — нафиг мне это надо, лучше я сам буду все делать, сам буду знать, что там творится и как. Если что сломается, то сам и починю. В какой-то момент просто приходит озарение.
Низкий уровень Си является огромным его минусом и всегда таким будет. Минусом это перестает быть только тогда, когда задача требует этого. Именно требует. Ядро ОС требует. Драйвера требуют. Игры — игры автора никаким местом не требуют. Это просто прихоть автора, ничем объективно не обоснованная. А вот игры ААА уровня, особенно для консолей — требуют. Но там Си никому не нужен, используют С++. Последний, собственно, нормально подойдет и для ОС с драйверами. Например, Apple для драйверов выбрала С++ и ООП интерфейс developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/Features/Features.html#//apple_ref/doc/uid/TP0000012-TPXREF101
Что до линукса, то смотря на их забавные попытки, по сути, реализовать C++ на Си в той же VFS, то там тоже не видно причин не использовать С++. Просто Линус против, да и время сыграло свою роль. В то время компиляторы вполне возможно могли быть не так хороши.grossws
16.01.2016 18:05+1Что до линукса, то смотря на их забавные попытки, по сути, реализовать C++ на Си в той же VFS, то там тоже не видно причин не использовать С++
А зачем там плюсы? Всё равно rtti и exceptions придётся отключать для ядра, плюс развлечения по борьбе с mangling, что приведёт код к Си-подобному. Хотя шаблоны были бы крайне приятны.
Ivan_83
18.01.2016 10:59+2«Низкий уровень Си» это не минус, это особенность языка, ради которой он в общем то и создавался и за счёт которой до сих пор живее всех остальных.
Кроме ядра и дров полный контроль необходим в архиваторах, кодеках, криптоалгоритмах и любых других алгоритмах где важна скорость выполнения/эффективность. Это противопоставление не ++ а скорее другим языкам.
ООП ещё и в новых дровах венды есть, как минимум попытка, там целый фреймворк для этого.
2 0xd34df00d:
Формально я тоже иногда пишу на ++: в вижал студии оно как то всегда по дефолту. Но ничего из ++ при этом не использую.0xd34df00d
18.01.2016 15:26+4Формально я тоже иногда пишу на ++: в вижал студии оно как то всегда по дефолту. Но ничего из ++ при этом не использую.
Не очень понял, причём тут это, ну да ладно. Я пишу на C++, фичи из C++ использую, и мои волосы всё ещё гладкие и шелковистые.
0xd34df00d
16.01.2016 18:58+2В случае Си мне всегда понятно что оно делает и как, никаких махинаций за ширмой, никакого волшебства, нюансы конечно есть но их довольно мало в сравнении с языками которые на себя берут заметно больше.
У меня полностью аналогичная ситуация, коллега! Только для C++.
А патчи к ядру или ещё чему то всем известному смотрятся круче, чем какой то там неведомой штуке на новом крутом языке :)
Да нормально всё смотрится в резюме. Даже личкрафты.
bigov
17.01.2016 10:19+5Спасибо за перевод, прочитал с интересом пост и особенно комментарии. Наверно автор мог бы сократить свой пост до строк: я пишу на ванильном С, потому что не смог до конца освоить ничего более сложного. Но в этом случае его пост бы не перевели для хабра.
a553
gwer
?\_(?)_/?
IRainman
Перевод же.