
Прошу прощения за задержку с ответом: обстоятельства.
AVC>>Имея почти двадцатилетний опыт программирования на Си и тринадцатилетний (с гаком) опыт программирования на Си++,
AVC>>опыт создания приложений в самых разных прикладных областях (от вычислительной математики до русского языка),
AVC>>опыт создания программ, работающих в жестком реальном времени времени, участвуя в создании систем безопасности,
AVC>>имея теперь уже немалый опыт создания ассемблеров, компиляторов (Си), отладчиков, вообще — систем программирования для новых процессоров, для некоторых из которых я был и первым программистом, не вижу в этой фразе Стауструпа ничего заслуживающего внимания.
AVC>>Уф!
E>Да, опыт внушает.
E>Но, имхо, как раз фраза Страуструпа к тебе и относится, т.к. твой большой опыт не позволяет тебе увидеть, что он (опыт) всего лишь маленькая капля в море.
(голосом кота Матроскина) Я еще и СУБД писал...
AVC>>Напротив, каждый день я вижу, как мучаются эти программисты, многомиллионым количеством которых здесь принято громко хвастаться.
AVC>>Ведь за консультацией по Си/Си++ они часто приходят ко мне.
E>Если перейти на личности, то я представляю себе, с каким отношением к C++ эти программисты затем уходят от тебя
Ну, не надо преувеличивать мои заслуги...
Все они взрослые люди, со своими (и неглупыми!) головами на плечах.
Одними словами их не проймешь. (К тому же, я ведь еще и помогаю сформулировать их идеи на Си++. Что бы это была за помощь, если бы я только повторял: "сами теперь видите, что Си++ — дерьмо". От этого большой Си++ный проект в одночасье не станет обероновским.)
E>Есть такое понятие, как unix way. Аналогично, есть понятие C++ way (хотя об этом книг и не написано). Так вот любая проблемма, которая тривиальным образом решается в каком-то языке программирования, но не имеет очевидного решения в другом языке просто говорит о том, что для этого (якобы ущербного) языка пытаются использовать чужой подход. Ниже я поясню это на примере.
E>Привожу реальный пример, в котором отсутствие массивов в C++ позволило существенно сократить трудозатраты за счет изобретения велосипеда. В методе конечных элементов необходимо решать СЛАУ большой размерности, в которой матрица обладает двумя важными свойствами:
E>- отличные от нуля значения находится в узкой ленте вдоль главной диагонали;
E>- матрица симметрическая.
E>Из-за этих особенностей выгодно хранить только верхнюю полуленту матрицы. Но, если выделить массив, который хранит только полуленту, то к элементам такой "виртуальной" матрицы уже нельзя будет обращаться по простым индексам [i,j] -- нужно делать их преобразования. Т.е., везде, где можно было бы записать:
E>E>a[i][j] = b[j][i];
E>
E>пришлось бы делать что-то вроде:
E>E>a[ get_i(i, j) ][ get_j(i, j) ] = b[ get_i(j, i) ][ get_j(j, i) ];
E>
E>Лично для меня второй способ был бы гораздо печальнее, т.к. ошибиться в нем гораздо проще. Поэтому, используя возможности C++, был сделан класс виртуальной матрицы, в котором обращение вида:
E>E>a[i][j]
E>
E>неявным для прикладного программиста образом преобразовывалось в:
E>E>a[ get_i(i, j) ][ get_j(i, j) ]
E>
E>Вот так, благодоря тому, что в C++ нет таких, казалось бы, основопологающих вещей, как матрицы, удалось получить простое, красивое и эффективное решение сложной задачи. И было сделано это где-то в 94-м году, когда и шаблонов-то в C++ я еще и не видел. А сейчас, на шаблонах, можно делать с матрицами еще более эффективные вещи. Правда, я этими вещами не занимаюсь, поэтому не могу дальше развить эту мысль.
ИМХО, никакой иной "пользы", кроме того, что это все это проделано
неявно для программиста, не видно.
Такая "неявность" скорее вредна, чем полезна (пример с
неявным (!) преобразованием во float через bool я уже приводил).
Чем хуже тривиальное get(a, i, j)?
Только тем, что несколько иначе выглядит? Так ведь это и хорошо!
В противном случае программист может быть просто
обманут, думая, что и правда имеет дело с массивом.
AVC>>А насчет вычислений: никуда Вам от них не деться. Даже индекс в массиве Вам приходится вычислять.
AVC>>А там где вычисления — там и потенциальные ошибки.
E>Никогда не сталкивался с потерей точности или ошибками вычислений при вычислении целочисленных индексов из целочисленных же значений.
И за границы массива
никогда не выходили?!
AVC>>Си++ не помогает с ними бороться. Скорее — наоборот.
E>Имхо, C++ позволяет достаточно удобно записывать то, что ты хочешь получить. Если при этом ты сам записал что-то не то, то C++ здесь не при чем. Не имеет смысла менять безопасную бритву на опасную, если не умеешь пользоваться последней.
E>>>>>Теперь объясни мне, где же я ошибся в определении причины катастрофы? Главное, что исключение возникло там, где его не ждали.
AVC>>>>Главное — что сняли защиту.
E>>>Нет, имхо, проблема была в проектировании, в том что было решено использовать оригинальные модули от Ариан 4 в Ариан 5. А уже то, что где-то был выполнен явный каст -- это уже следствие.
AVC>>Из отчета, ссылку на который дал Cyberax (за что ему отдельное спасибо, хотя не помню, чтобы мы хоть раз совпали во мнениях
), очевидно, что первопричина — в недостаточности вычислительных ресурсов. Соответствующую цитату из отчета я уже приводил.
AVC>>Если бы с ресурсами было все в порядке, вычисления производились бы с использованием соответствующего типа, а не 16-битного знакового целого; и все преобразования, если бы они потребовались, были бы защищены от подобных исключений.
AVC>>А так программисты вынуждены были искать, где пожертвовать надежностью.
E>Так ведь это обычные условия. В реальной жизни всегда чего-нибудь не хватает.
Одно дело, когда "чего-нибудь не хватает" в офисном приложении.
Другое дело — в аэрокосмической, ядерной или военной областях.
Я бы на Вашем месте не был настроен столь философски.
В главной же Вашей мысли, что это не столько ошибка кодирования, сколько большая проектная ошибка, я с Вами согласен. В свободное время япытался представить себе, что именно я бы сделал на месте программистов Ариана-5 в тех условиях. Ничего утешительного не придумал...
Но, кажется, отсюда мы с Вами делаем разные выводы. Вы говорите, что так было, так есть, так будет (или нет?). А я говорю, что, если не хватает ресурсов для нормального (защищенного и типо-безопасного) программирования, лучше вообще не браться за подобные проекты.
E>Это не дурдом, это особенность.
Я, наверное, эту Вашу фразу на стенку повешу.
Насколько здесь выразительнее сформулирована старая мысль "это не баг, это фича".
E>Точно такая же, как необходимость объявлять переменные в Pascal-е в специальной секции var, а не в том блоке, где она необходима.
Вообще-то, в Паскале, Модуле и Обероне переменная объявляется
именно там, где нужна.
Просто некоторые Си++программисты не в курсе, как именно это делается.
Допустим, я хочу провести поиск какого-нибудь значения в массиве.
Ясно, что для этого мне потребуются дополнительные переменные, которые может быть больше в данной процедуре нигде не нужны.
Я просто выношу поиск во вложенную процедуру:
PROCEDURE GlobalProc(v: INTEGER);
CONST size = 1024;
VAR a: ARRAY size OF INTEGER;
PROCEDURE LocalSearch(x: INTEGER): INTEGER;
VAR i: INTEGER; (* здесь могут быть объявлены и другие вспомогательные переменные *)
BEGIN
FOR i := 0 TO size-1 DO
IF a[i] = x THEN RETURN i END
END;
RETURN -1
END LocalSearch;
BEGIN
(* ... море кода, где переменная i не нужна *)
IF Local(v) >= 0 THEN (* ура, значение найдено! :) *)
ELSE (* увы, не найдено... :( *)
END;
(* ... еще море кода, где переменная i не нужна *)
END GlobalProc;
А теперь важное замечание. Если нет рекурсии, и программист не запрещает, то компилятор сделает вложенную процедуру
подставляемой (inline). Так что нет ни потери ясности, ни потери эффективности.
А вот в Си++ сидишь и гадаешь, как изволит компилятор отнестись к конструкции
for (int i = 0; i < size; i++) ;
Будет ли переменная i "жива" и после цикла, как в Visual C++ 6.0, или нет, как полагается по стандарту?
AVC>>Чтобы там не провещал Страуструп, я еще не разучился различать черное и белое.
E>Имхо, мир гораздо богаче оттенками, чем только черное и белоее. Развивая эту мысль хотел бы провести такую аналогию: программирование на паскале-подобных языках -- это рисование фломастерами, нужный оттенок можно получить, только если он есть в наличии (ну, или если есть очень большой опыт и везение, скажем зеленый цвет можно получить проведя синим фломастером по следу желтого). Но даже рисование простым карандашом (что-то похожее на C) дает гораздо больше выразительных возможностей. C++ -- это уже масляная живопись -- много мудоты, краски долго сохнут, свежий слой можно класть либо на свежий еще слой, либо на полностью просохший слой. Писать можно только на специально прогрунтованных поверхностях (картоне, досках, холстах, стенах, на простом ватмане уже не получится). Зато какой результат в умелых руках получается! А Java/C# -- это темпера (хотя сам не пробовал, только читал) -- вроде почти тоже, что и масло, но ярче и сохнет быстрее, разводится простой водой, менее требовательна к поверхности. Но вот с полутонами и полупрозрачными слоями там беда, до масла далеко.
Очень интересная параллель!
Знаете, это достаточно забавно, но среди Си++программистов достаточно много людей с художественными (но, к сожалению, не математическими) наклонностями!
Один мой добрый знакомый — действительно гениальный Си++программист (по крайней мере, второго такого я не встречал) — пытался как-то мне объяснить процесс своего мышления над программой. Сначала он немного помычал для внушительности, затем стал решительно тыкать пальцем в закатное небо и перечислять, какие оттенки и особенности он там видит. Сознаюсь — я мало что понял, но зато запомнил эту картину закатного неба на всю жизнь!
Я мог быть развить эту тему разных стилей программистского мышления, с их плюсами и минусами, но пусть лучше остается все как есть — в виде яркого воспоминания.