Pull to refresh

Comments 411

Хорошие заметки.

Преждевременная оптимизация хуже преждевременной эякуляции
UFO just landed and posted this here
Ну я же говорил о преждевременной…

Тем более узким местом обычно является не так много кода, там можно приложить все силы
UFO just landed and posted this here
мне вообще фраза нравится.

«преждевременная» оптимизация должна быть на бумаге, и быть результатом целенаправленного труда, а не побочным эффектом
Имеется в виду один из постулатов Суттера — а именно, что большинство программистов на плюсах не до конца понимают, как это ни печально, что именно они делают и поэтому они как бы «оптимизируют» код, а на самом деле выигрыш в скорости бывает нулевым, если не отрицательным
Я думаю, что тут некоторая путаница в понятиях «преждевременный» и «ранний». Последнее бывает как и своевременным, так и преждевременным. Как-то вот так.
Я смотрю, у Вас это стало коронной фразой. Подозреваю, ещё не раз её увижу в комментах с Вашим авторством.
Преждевременная пессимизация хуже импотенции

final fix
Снимаю шляпу перед вами — хорошие слова, еще Фаулер говорил что если проект хорошо спроектирован то он легко поддается оптимизации — но только после того как будет проведено профилирование и будут известны узкие места — а то нынче оптимизация ради оптимизации и вообще это модно)
я тоже в школе этим болел. даже делала так:
char str[10];
char *p = str;
...
str++;
...
if( *p == ' ' )
...

Я просто скажу, что в телефонах отнюдь не 2-4 Гб памяти, пока )
В начале статьи я отдельно отметил, что речь идет о программировании под десктоп-операционки.
Часто приходится под всё сразу программировать, поэтому некоторые фичи сразу отметаются, а на мобилах еще часто и компиляторы на наскальную живопись похожи.
Только хотел задать вопрос о кросскомпиляции, как проверить правильную работоспособность…
Уже не буду.
Грустно.
Ага, и даже на терминалах (сбора данных) пока, к сожалению. Бывают и stack overflow, и low memory. А по сети передавать с них данные — отдельная сказка.
По-мойму для телефонов через год это уже мало будет. Глядя на нынешние телефоны, которые уже покруче старых компов будут.
Вы посмотрите на apple. Они каждый Вт*ч экономят. У них не более 0.5 Гб
Может и покруче, но некоторые демонстрации различных эффектов, которые я наблюдал еще на 286х, почему-то круто тормозят на телефонах «покруче». Процессор явно сильнее, видеокарты тоже, а тормоза, словно запустил Descent на 486 DX2 66…
причина — экономия на памяти
а про Descent на 486 DX2 66 правильно подмечено.
Может причина в отсутствии оптимизаций как таковых? Рост объемов памяти, скорости процессоров, видеокарт, шины передачи данных развратило рынок. С одной стороны это неплохо, забыв про ограничения люди наконец начали создавать продукты, которые работают уже сейчас, и не нужно ждать пол года, чтобы получить работающую версию. С другой стороны аналогичные подходы к разработке неприменимы в условиях ограниченных ресурсов. В таких условиях оптимизация не просто играет роль, она жизненно необходима с самого начала.
Лично меня удивляет, как на телефонах тормозит слайдинг одного рабочего стола на другой. Для этой операции хватит мощности 286-го компьютера даже без участия видеокарты. Откуда такие тормоза?
очевидно из-за не оптимальных библиотек
я — разработчик WEb-приложений под высокие нагрузки.
стараемся меньше использовть сторонних библиотек
Ну так переписать библиотеки надо. Да, я понимаю, что звучит нагло, но это тому же Гуглу будет дешевле.
я при оптимизациях и поиском узких мест под Symbian OS очень удивился, что процентов эдак 90-95 процессорного времени тратилось на ожидание устройства ввода/вывода. постоянная память на телефонах очень медленная, это, бывает, не учитывают
Архитектурная ошибка?
Я, честно, не ожидал, что seek в файле + запись в его конец (точнее перетирая с конца ~2 байтов + за пределы файла) ~100 байт будет отрабатывать 200-300 мс вместо 1-2 мс на десктопе
Да все к тому идет. Пару ядер и несколько гиг памяти — даже в брелке.
UFO just landed and posted this here
Ко всему прочему, C++ настолько красив, что позволяет оптимизацию сложить на плечи компилятора (vector например).

А уж если делать наскальную живопись, то надо ее располагать в одном месте, а не размазывать по всему коду
UFO just landed and posted this here
столько вкусного, но я на С пишу :(
Когда нужно добиться максимальной производительности, тоже пишу на C, плюсам не доверяю) Мне почему-то кажется, что многие фичи C++ тормозят программу (если сравнивать с аналогичной на C), хотя многие говорят, что это миф. А так, если производительность не нужна, то и мучится с C смысла нет.
Да. Это миф. Не многие, а только определенные.
это не миф, но таких задач крайне мало
мы делали замеры: Использование fwrite/fread на 20% производительнее iostreams
а время на форматирование строки учитывали?
Вы путаете с fprintf :)
а вы бинарные данные писали?
А что ещё можно писать при помощи fwrite/fread и iostream? Man бы хоть прочли.
iostream у нас перестал поддерживать operator <<?
Ну… Я имел в виду iostream.{read,write}, явно же с ними сравнивали прозиводительность. Иначе, не разумно.
тогда интересно было бы узнать причины… не пробовали понять?
Ну. Я пробовал. Но iostream (в libstdc++) написан настолько trueЪ-хардкорно, что я не осилил и забросил это дело. Но скорее всего, какие-то плюсовые накладные расходы. Может быть, даже на поддержку тех же исключений.
про расходы на поддержку исключений — это бред. особенно если код генерится gcc — там будет такой же перехват исключений
Исключения будут в fread/fwrite? Там их не написано.
ну вообще компилятор очень редко может предполагать это
Ну так fwrite/fread более низкоуровневая обёртка к системному вызову write/read (даже прототипы почти одинаковые), чем iostream, поэтому и работает быстрее.
Какое чудесное у вас Пи:
#define PI 3,1415926535897932384626433832795

Не буду говорить, что получится, если сказать, например,
x = PI/2;
:) скопировано из виндовс-калькулятора. Сейчас поправлю.
Когда-то сделал точно так же и полчаса баг искал :)
Интересно, за что вам поставили минус…

Разверну ваш ответ для тех, кто так и не понял: фишка в том, что разделителем целой и дробной части в всех [известных мне] языках программирования является точка, а не запятая. А макрос определён именно с запятой:

#define PI 3,1415926535897932384626433832795

Таким образом, при разворачивании макроса в выражении

x = PI/2

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

Очередной аргумент в пользу использования типизированых констант вместо макросов ;)
Сказать Линусу Торвальдсу и другим ядерщикам, что они живут в каменном веке. :)
Вот его известное письмо:
thread.gmane.org/gmane.comp.version-control.git/57643/focus=57918

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

C++ хороший способ переложить на компилятор заботу о многих вещах, о которых вы вынужденные думать в С, без потери производительности.
Вполне можно огриичиться перегрузкой, шаблонами, и не использовать runtime фишки (динамичскую полиморфность, RTTI).
А те же исключения вообще замечательная вещь. И может быть реализованно с нулевой стоимостью для правильного потока исполнения (в С вы всегда должны проверить возвращаемое значение).
Раскрутка стэка по исключению и связанные с ней накладные расходы обходятся дороже, чем проверка возвращаемого значения (http://ddima.livejournal.com/67706.html, разница на синтетическом примере порядка 35%)
apple для obj-c исключений рапортовали о нулевой стоимости.

Хорошая идея проверить это для gcc.

ЗЫ: я имел ввиду нормальный путь исполнения, а не раскрутку стека
Надо будет посмотреть да.
По поводу ЗЫ: дело в том, что, для того, чтобы раскрутить стек, нужно иметь соответствующий механизм, который требует ресурсов и не так уж и мало — пройдитесь по ссылке, посмотрите http://ddima.livejournal.com/67706.html
Что-то быстрый взгляд (а тут еще заказчик мозг имеет) не увидел стоимость использования исключения при нормальном исполнении кода
Сорри, попутал ссылку ;)
Не настолько я крут в этом. Но заинтересовало. В связи с отсутствием винды, буду тестить clang/gcc. Но когда — не знаю.

Вообще, как мне кажется, в винде еще надо и системные исключения перехватывать (забыл как они зовутся, типа превышения стека, SEH вроде). И, мне почему-то, всегда казалось, что в винде есть необходимость именно докладывать данные на стек.
На самом деле не все так страшно. Главное помнить, что исключения должны быть именно искючениями. Что же касается тормозов в конкретных версиях компиляторов — это проблемы этих конкретных версий. Потенциально исключения, до тех пор пока они не вызваны, не должны замедлять работу программы в хорошо спроектированном компиляторе.
Ну. Как оказалось, до mac os x 10.5 компилятор obj-c использовать *jmp функи для перехвата исключений, и это замедляло (хоть и не сильно) нормальное течение процесса.
Как сейчас сделано, не знаю. Но думаю, что уже весь mainstream давно научился не замедлять код. Ведь C++ всегда должен ожидать получения исключения — деструкторы должны отработать.
Нулевая стоимость использования исключений — миф. Наличие исключений может сильно ограничивать оптимизацию, даже если эти исключения никогда не срабатывают.
Интересно. А можно поподробнее?
По хорошему это статью надо писать про это. Если коротко, то границы try-catch региона являются препятствием для оптимизаций. Например, иногда нужно поднять загрузку как можно выше, чтобы к использованию данные были уже готовы, то граница try-catch региона может ограничить такую трансформацию. Оптимизации циклов, по-моему, вообще отрубаются в большинстве случаев, когда внутри циклов есть исключения. Так происходит потому, что практически любая трансформация цикла может изменить поведение исключений (допустим если мы сделаем fusion циклов, в одном из которых есть исключения, то результат будет совсем не предсказуем с точки зрения программиста). Что там происходит с аллокацией регистров точно не знаю, но подозреваю, что там тоже возникают свои «особенности».

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

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

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

Но 99% кода не перехватывают исключение. В этом случае допустима полная оптимизация. И этот код от исключений только выигрывает.
Не совсем верно. Наличие try-catch накладывает больше ограничений, нежели возбуждение исключений. Но возбуждение исключений тоже препятствует оптимизации. Хотя, по сути, исключение эквивалентно обычному вызову неизвестной компилятору функции, который точно также препятствует оптимизации (не все об этом задумываются). В этом случае возможность исключения в цикле предполагает возможность «незапланированного» выхода из цикла и это, например, может помешать параллелизации цикла или фьюжену с соседним «безопасным» циклом.

На счёт деструктора я не совсем понял вашу мысль.

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

Таким образом всегда должна быть возможность обработать раскрутку стека
Это верно. Именно поэтому не стоит звать в продакшн коде в хотспоте всякие дебаг функции, которые тем более ещё не видны компилятору :)

Кстати, компилятор распознаёт многие вызовы к стандартной библиотеке и считает их безопасными (если они действительно безопасные, конечно).
Чаще всего ты вызываешь свой код. А он не безопасен по его мнение

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

Если же код определён в другом модуле, то может спасти межпроцедурная оптимизация. В интеловском компиляторе это ключик -ipo, у gcc начиная с 4.5 это LTO (link time optimization, какой ключик не знаю).
Это да. Но все это имеет смысл для часто вызываемых малых функций.

В любом случае, если вы в массиве обрабатываете чтения из 100 файлов, то оптимизация вас не спасет.

Но общая идея понятна.
+1
Почитал комменты, и вижу, что мало кто задумывается, как можно реализовать все многообразие функциональности исключений «с нулевой стоимостью».
Почитайте, например, как сообщество IA64 предлагает реализовывать исключения:
www.codesourcery.com/public/cxx-abi/abi-eh.html
Особенно обратите внимание на «Runtime initialization», и пр. «мелкие нужды».
По ссылке просто ABI, не совсем понял как это относится к делу. На x86 всё тоже самое по сути. Если вы про то, что на самом деле там куча всяких ньюансов в технической реализации и сама реализация включает в себя кучу всяких таблиц и дополнительной информации в объектниках — это да.
UFO just landed and posted this here
Лучше? Чем? затратами при нормальном исполнении?
UFO just landed and posted this here
Слова «штатная» и «исключение» являются скорее антонимами.
Использование исключений во время штатных ситуаций — это все равно что goto в коде. Насколько мне известно, только в Python исключения используются повсеместно, во всех остальных случаях — они достаточно редки.
я использую исключения для указания отказа пользователя от продолжения ввода данных.

но, при ошибке, не так критично время — критично важно правильно ее обработать.
UFO just landed and posted this here
В итоге получается так, что «запись не найдена» и «недостаточно памяти» — это события одного уровня.
Т.е. опустив исключения на уровень работы в штатных ситуациях, вы потеряли однозначный механизм для сигнализации о серьезных проблемах.

Не уверен, что это хорошо.
Почему же. Исключения типизированы. Значит могут быть обработаны разным способом.
Я вижу тут возможные проблемы с инкапсуляцией (необходимо знать внутренние особенности функции/модуля), либо с дальнейшим изменением (что если вы обновили библиотеку, и она начала выкидывать новый тип исключения, который вы не предусмотрели? )
Если вы не предусмотрели какой-то тип исключения, то это значит, что в этом месте вы его не можете обработать — пусть летит дальше
А если это «штатное исключение», которое можно и нужно было обработать на этом уровне? Оно ведь у вас полетит на самый верхний уровень, тот, где обрабатываются серьезные ошибки, вроде «Cannot allocate memory».
UFO just landed and posted this here
Штатная ошибка — это ошибка, которая может быть обработана.
Если ее точка возникновения — вышележащий код, то такая обработка может быть сделана на месте без исключений. Иначе, выбрасывается исключение и проблема снимается с этого участка кода.
UFO just landed and posted this here
в крайней случае он вызывает std::terminate
А как вы определите, является исключение критичным, или нет? Придется вводить список критичных или некритичных исключений и поддерживать его в актуальном состоянии. Чревато ошибками, не так ли?
Есть же наследование. Вот его и используйте.
UFO just landed and posted this here
UFO just landed and posted this here
А кто мешает делать локальные типы исключений?
UFO just landed and posted this here
Предположим, что кодер Вася изменил функцию поиска записи, и она начала выкидывать новое исключение: «Найдено две записи». (Предположим, что он пишет инструмент для анализа целостности БД). Что произойдет с двумя вашими функциями?
UFO just landed and posted this here
похоже, хабр нуждается в статье про исключения.

желательно, без спецификации языка
Таким образом, логика первой функции уже возможно, когда-нибудь, будет нарушена. Вот это меня и смущает.
Для того, чтобы вернуть функции прежнее поведение, придется городить огород из try...catch, не так ли?
зачем?

поведение функции, которое нас интересует:
* функция выполнилась
* функция не выполнилась по причине такой-то ошибки (если мы хотим ее обработать)

Все другие ошибки нас не интересуют.

Или я просто не понимаю, что вас смущает
Меня смущает возможная быстрая раскрутка стека на самый верх в ситуациях, где этого можно было заранее гарантированно избежать. Частично может почь finally, но лишь частично и не во всех языках (
(Вот только в этой дискуссии понял, почему есть такие функции как get_last_error())

Смотрите, псевдокод, который использует возврат значения:

result = someFunction();
if (result == RESULT_ERROR){
resultCode = getLastError();
switch (resultCode){
// Какой-то код, который обрабатывает ошибки
RESULT_ERROR_FILE_NOT_FOUND: someAnotherFunction(); break;

default: throwError();
}
cleanup();
}


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

Зачистка стека, в идеале, должна происходит автоматически. В С++ это делается с помощью деструктора. То есть все временные состояния должны автоматически зачищаться при раскрутке.

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

Вы же не хотите, чтобы из-за неожиданного исключения, скажем в системе записи логов, программа падала по Unhandled exception?

Т.е. вот такая функция может вызываться всегда и не прервет выполнения потока:

// Returns 0 on success or -1 on failure
function log(message){}

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

// Throws WriteException on failure
function log(message){}

log — это только пример.
Она падает по не «Unhandled exception», а по причине: невозможно записать в такой-то файл.

Если функция не должна валить выполнения кода не когда, то она должна быть объявлена throw(). Но тогда ей нет смысла и возвращать ошибку — вы ее не обработаете.

А теперь представьте, что кто-то напишет

result = f(smth);

if( result == err1 ) {
do_cleanup();
return err1023;
}

do_smth();



Или вообще не проверит код возврата, потому что ему кажется, что это не важно (или это действительно не важно было в первой версии функции, которая обязательно делала что-то, а потом ее функционал расширился)
А если возможность записывать в такой-то файл является некритичной для приложения в целом?

А если код возврата действительно не важен? В том то и разница — если код возврата не важен, его можно игнорировать. Исключения же игнорировать нельзя — их всегда надо обрабатывать, иначе — fail. В итоге, использование исключений увеличивает связность кода. С другой стороны, это заставляет обрабатывать многие ошибки во время разработки… Палка о двух концах, как всегда ;)

P.S. Хороший холивар, но я пошел домой ;)
не повышает, а понижает. Ибо код делает только то, что от него просят
UFO just landed and posted this here
UFO just landed and posted this here
Это может работать в других языках, в которых слово «производительность» попадает под табу, зато все улыбаются и машут.
Но в С++ приведенный вами пример однозначно считается очень плохим дизайном.
В C++ тоже хорошим считается, если функция делает то, что ей говорят.

Если create — то либо create, либо исключение

Тогда можно без проблем писать код:
obj = create(smth);
obj.make_smth();
Это, как раз-таки, зависит от дизайна.
Если функция create может сфейлиться только из-за вселенских катаклизмов типа нехватки памяти, то она возвращает ссылку в случае успеха и кидает исключение в случае фейла. Вызывающий код в этом случае может обойтись без лишних проверок.
Если же там что-то сложное и нетривиальное, например, проверка входных параметров, то функция create возвращает указатель, который очень даже может быть 0, если что-то не так. И вызывающий код обязан проверять этот указатель.
Проверить? зачем?

Функция или отрабатывает, либо выбрасывает исключение. Не каких проверок вовне нету. Вот этот правильный и простой дизайн
Это прикладная мифология, что проверок нет. Чтобы сгенерировать исключение, всё-равно должен быть написан где-то if с анализом некого состояния системы. То есть, при использовании исключений для обработки ошибок, вы получаете if + накладные расходы на оформление кадра вызова функции, чтобы была возможность из неё откатиться.

Конечно, иллюзия создаётся, что никак нет if-ов, но это только иллюзия. Они просто перенесены из одного места, с анализом возвращаемых кодов, и рассованы по коду, который throw делает. Но это для оптимизации не самая лучшая ситуация. Компилятор лучше справляется с оптимизацией переходов, если они явно выстроены в деревья, а не связаны какими-то неявными goto.

Исключения следует использовать именно лишь как исключения. Когда, например, в какой-нибудь библиотеке не предусмотрен интерфейс для обработки определённой ситуации, которая может возникнуть, когда Вы ей подсовываете свой объект. Ну, или когда языком не предусмотрено
возвращаемое значение. Операторы там, конструкторы.

В остальных случаях использовать исключения — это убивать производительность.
Абслютно с вами не согласен. Причины написаны выше. Но повторю:
* проверка идет только в самой низкоуровневой библиотеке
* любая функция уже заранее рассчитана на то, что через нее летит исключение
* исключение содержит куда больше информации об ошибке
* исключение как раз снижает связанность кода. При использовании кодов ошибок при добавлении возможной ошибки во вложенных функция может вырасти количество возможных ошибок в самой фукнции — и так по всему дереву вызова
проверка идет только в самой низкоуровневой библиотеке

Э? Если только там, значит это какая-то примитивная программа. Обычно, всякие ситуации, не дающие продолжить расчёт, и в других местах возникают. Я правильно понимаю, что низкий уровень — это всякие read/write/malloc?

А всякий throw будет обёрнут в if. Но и более того, вы сами говорите, что

любая функция уже заранее рассчитана на то, что через нее летит исключение

А для этого она должна быть особым образом оформлена. При каждом вызове в стек при вызове любой функции, которая потенциально выкидывает исключение, должна быть записана дополнительная информация. А это гораздо накладнее, чем дополнительный cmp/jmpсс (который предсказывается процессором, как не взятый) при возврате из функции.

Для оптимизаций тут в C++ даже предусмотрена специальная сигнатура для функции throw(), которая говорит, что эта функция ничего не пробрасывает через себя.

исключение содержит куда больше информации об ошибке

Кто заставляет при обработке ошибок возвращать int? Можно возвратить указатель на структуру любой сложности. NULL — ошибок нет, иначе уходим на разбор.

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

Так при оптимизации как раз и хорошо, то что связанность высокая. Если мы знаем, что в каком-то месте возник в условии if некий fasle, то можем просчитать на какой else он в итоге нас выведет, после прохода по цепочке других if-ов. оптимизацию jump-свёртки можем делать. С исключениями такого не провернуть.

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

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

просто сейчас реально возразить времени нет. соглашусь только с тем, что использование исключений может наложить дополнительные требования и усложнить вызов функций. тот же throw() еще более ресурсоемкий, потому что ему, кроме того, что обработать полученные исключения, надо еще и перехватить любое из исключений внутри, и бросить std::termnate в случае ошибки.
А в чём предмет спора? Вы же сами подтверждаете тезис о том, что функция уже совсем не прозрачно вызывается. Там нужно кое-что дополнительно писать в память, а это дороже, чем cmp/jmp (заранее предсказанный).

Собственно, миф об отстутсвии накладных расходов при прямом (безошибочном) ходе программы разрушен.

В чём я не прав?.. Может, кинете ссылку на то, где Вы об этом писали?
я не сижу причины, почему должны быть накладные расходы.
В смысле? А как вы свяжете throw с нужным try-catch? Эта связь — некая функция, называемая, функцией персонализации. Так вот, указатель на неё (в лучшем случае, некоторые компиляторы ещё сохраняют дополнительные параметры) и надо дополнительно сохранять в стеке (ну, или дополнительной таблице, это уже от компилятора зависит), каждый раз вызывая функцию, которая потенциально может нечто выбросить. И это, на самом деле, жуткий ужас по накладным расходам. Запись в память — дорогая операция.

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

Я думаю, что процесс идет так:
* бросаем исключение
* идем по стеку, смотрим, какие функи были вызваны
* смотри, есть ли в них блоки раскрутки исключений
* вызываем если надо
* идем дальше
проход назад. какое нам дело до цены этого?
UFO just landed and posted this here
Эх. Но возможность раскрутить стек не даётся за бесплатно. Чтобы его раскручивать, нужно же знать, где находить Program Counter'ы (PC) для возврата.

Стеки при этом все разных размеров поэтому нужно, как минимум, хранить ссылку на предыдущий кадр (например, если речь о x86, то это push ebp; mov ebp,esp в прологе функции). И это никуда при оптимизации не выкинешь. Код, который не выкидывает исключений, часто запросто без этого обходится (у gcc даже опция на этот счёт есть -fomit-frame-pointer).

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

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

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

Как я уже много раз повторял, тут цена вопрос — это две коротких инструкции cmp (или даже test) и jmp, который будет предсказан, как не взятый. Для процессора это выполнить проще, чем организовать дополнительную запись в память.
Да. С этим соглашусь. А если надо сделать проверку десятка разных кодов?
Не знаю… Я всегда стараюсь минимизировать количество этих кодов, и использовать что-то вроде той обработки, которую применяли в исходниках half-life 2, или которая предлагается в Go.

Там ошибка — это некий объект. Если всё хорошо, то возвращается NULL, если произошла ошибка, то возвращается этот объект, который уже можно долго поанализировать. Тогда цепочка кода:

err = do1st()
if(!err) {err = do2nd()}
if(!err) {err = do3rd()}
if(!err) {err = do4th()}
if(!err) {} else {process(err)}

вроде как быстро пролетает, когда ошибка не возникает. А когда возникает, то всё уходит на последний if, где можно уже структуру err поковырять в зависимости от ситуации.

То же самое, можно писать и с goto (так, на самом деле, и пишут в основном)

if(err) goto errStageX

но за goto фанаты 'правильности' могут и побить.

Да, несколько громоздко, конечно. Но эффективнее, чем исключения. И с логической точки зрения, вроде, даже удобнее. Потому что, ошибку можно учесть там, где она возникла: закрыть файлы, отрубить сокеты… А не размазывать это всё по всему дереву вызовов.
UFO just landed and posted this here
UFO just landed and posted this here
Так проблема, к сожалению, в том, что функция понятия не имеет, что за виртуальный метод она вызовет. И откуда её вызвали. Может, она вызывает тот метод, который может выкинуть исключение, которое ждут где-то наверху. Поэтому код должен быть почти всегда готов, особенно если оперирует с объектами с виртуальными методами.

Поэтому игроделы, например, виртуальные методы особо не жалуют. Стараются избегать.

В общем, компилятору в этом анализе приходится туго, и он не всегда справляется.
UFO just landed and posted this here
Упс. Ответил не туда. Ответ был к посту выше.
UFO just landed and posted this here
Все в мире относительно, «медленные» исключения Си++ быстрее любого оператора Питона:)
Не всегда так. Вполне может быть оправдано написание функции createOrOpen
UFO just landed and posted this here
ну. или быть организовано низкоуровнево.
или сделать проверку перед созданием. и тд
По Страуструпу исключения должны выбрасываться только в случае критических ошибок, которые функция не имеет возможности как-то вменяемо обработать.
Ситуации типа «файл не найден» или «юзер ввел не те данные» — это нормальные штатные режимы работы, которые надо нормально же обрабатывать в коде.
Исключения же — это ситуации типа внезапной нехватки памяти, потери точности в вещественных вычислениях или bad_cast, когда оказалось невозможным приведение типов в рантайме.
Вот тут я с вами не соглашусь.

Ситуация «фаил не найден» может быть как штатной, так и критической ошибкой
Ну понятно что ситуацию можно выдумать любую, но в большинстве случаев «файл не найден» это именно ошибка, а не исключение.
У вас есть процесс обработки файлов. Нужно считать данные из 10 файлов, совместить их и залить в третий фаил.

Вдруг не найден один из файлов. Как код, который ДОЛЖЕН считать данные из него, может адекватно обработать ошибку?
Ошибки надо обрабатывать там, где их можно обработать (привет, кэп!)
Откуда у вас берутся эти 10 файлов? Из командной строки? Из конфига? Из гуя? Вот в этом месте их и нужно проверять и открывать. И если что-то не так — материть юзера.
А если фаил поврежден. Если его структура невалидна?
Это тоже проверять в точке ввода данных?
Вопрос в доверии, если доверяем тому, что файл точно есть, а его нету, то это исключительная ситуация, а пользователю доверять априори нельзя! Посему любой неправильный ввод — это штатная ситуация.
А является ли штатной ситуацией нарушение формата файла? Пользователю то доверять нельзя.
UFO just landed and posted this here
Вообще, исключения типа фаил не найден, обрабатывает в самом низу (если говорить о gui), тем самым прерывая всю задачу, данную пользователем.
И это правильно
UFO just landed and posted this here
Ну вот тут специфичная обработка. Он ОЧЕНЬ редко в GUI нужна
>А ведь исключения выбрасываются далеко не только в случае критических ошибок — это вполне штатная ситуация, так сказать, способ коммуникации между функциями.

Как только я прочитал эту фразу, я сразу понял, что далее будет дли-и-и-инный тред на эту тему. Я не ошибся :)
когда есть куча библиотек, лучше использовать единый общий с библиотеками подход, а не думать каждый раз, вызываешь ли ты библиотеку или свой код, будет он возвращать исключения или не будет. Меньше возможности ошибиться, если использовать одинаковый с библиотеками подход.
я всегда считаю, что может произойти исключение. Но в 99.9% тебе не надо даже задумываться об этом
не понял, при чем тут это?
к тому, что когда «всегда считаю» следует учитывать и такие тонкости. Т.е. это не возражение какого-либо вида, просто дополнительная инфа по теме :-)
Ну если ты творишь такие вещи, как перехват всех исключений — то ты ССЗБ.

Хотя я творю, но там у меня «легковесные потоки»
А если посмотреть на стайлгаид гуглa — динозаврики, ни дать ни взять:

google-styleguide.googlecode.com/svn/trunk/cppguide.xml

То же и с Qt, mozilla, многими другими проектами.
Пример довольно успешного пректа, мощно использующего фичи С++ — llvm.org
Рекомендую к прочтению кода. Лучше многих книг.
Я в начале статьи отметил, что она не относится к системному программированию, а это именно то, чем занимается Линус и компания. Но Вы, конечно, правы — хороший или плохой код можно написать в любом языке.
Ядерщики не на плюсах пишут. Там чистый Ц.
На картинке бага?
#include void main() < — может так все-ж? Или C++ настолько суровей C?
че-т сожралось…
#include iostream
void main()
вообще по стандарту С++ функция main возвращает int.
error C4430: missing type specifier — int assumed. Note: C++ does not support default-int
Да там еще и пространство имен std куда-то затерялось )
Во времена <iostream.h> его не было… в том же Borland например.
Я настолько был уверен, что «Hello world» не получится написать так, чтобы не возникло вопросов, что скопировал его из соответствующей статьи Википедии. А всё-равно не помогло :)
C++ настолько суров, что даже в хелловорлде можно наделать пяток багов %)
Там ещё пространство имен std не указано. :3
По старинке возвращаемый тип у функции int. Наследие С. ISO C++ запрещает такое объявление:

error: ISO C++ forbids declaration of 'main' with no type

Конечно, стоит использовать -Wall или аналог.
Обычно в подобных постах вспоминают страшное «проклятие» C++: обратная совместимость с C.
И действительно, люди учившиеся давно или даже недавно, но у преподавателей учившихся давно, программируя на C++ все равно пишут код на C, пытаясь его «обернуть» в какие-то C++ конструкции, не понимаю того чего делают, чего теряют и какой кошмар творят. Такое происходит сплошь и рядом.
Также я буду говорить только о прикладном программировании под десктоп-операционки (в кластерах, микропроцессорах и системном программировании тенденции могут отличаться).


Очень хорошо, что Вы сделали эту оговорку. Использовать STL и Boost в системном программировании, конечно, заманчиво, но далеко не всегда представляется возможным.

UFO just landed and posted this here
>Счет памяти и ресурсов процессора шел на байты и такты, многие вещи еще не были изобретены и приходилось выкручиваться. Но это вовсе не повод и сегодня писать код исходя из этих предпосылок.

Вот благодаря таким умникам мы имеем многочисленных монстров на десктопов, которые чтобы отрисовать пару менюшек требуют не меньше 2Гб рамы. Особенно в линупсе замечал: кде4, гноме, фаерфокс (вообще взрыв мозга), опенофис (!!!!!) жрут всю память что находят и еще в свап зарываются. И при этом все тооормооозит и глючит.
Современных разрабов нужно запирать в комнате с компом в котором 128мб оперативки и пусть кодят и работают на нем.
UFO just landed and posted this here
Программистов надо научить одной, но самой сложной вещи: думать
… и спустя 5 лет они напишут все то же самое что и сейчас, работающее на 128 памяти, когда прогресс уйдет далеко вперед, а цена на 4гб памяти упадет с нынешних 1300 рублей до каких-нибудь ста рублей…
а потом появится новое поколение, программисты которого будут писать код, жрущий уже 6 гб просто так, «чиста по приколу»?)
Просто поставьте себе больше «рамы», она сейчас не дорогая. Зачем «разрабов» мучить?
Следствием апгрейда компьютера хотелось бы видеть возможность запустить еще больше тяжелых программ одновременно, видеть улучшение времени отклика интерфейса, времени выполнения задач.
А получается, что платишь, платишь деньги за апгрейд, а работает все примерно с такой же скоростью и все так же запустить более 2-3 тяжелых приложений даже на 4-6Гб рамы является извращением. Зато программистам легко, да)

З.Ы. Прошу не считать этот коммент холиварным, ибо я абсолютно согласен с тем, что для каждой задачи — свой инструмент; я не стесняюсь использовать дотнет, когда надо быстро набросать гуевое приложение под винду.
Запускайте старые версии, вам же достаточно тех возможностей.
угу, и потерять возможность открывать файлы, созданные другими людьми в более новых версиях; в случае с виндой например — потерять возможность запускать вообще любой новый софт; в случае с линухом (и не только) — отказаться от поддержки нового железа…
Список можно продолжать… Мы все вынуждены переходить на новые версии софта.
Ну насчет открытия файлов созданных в новых версиях, можно просить их экспортировать в какой-то другой формат.
Насчет винды, тот же .net не слабо упрощает разработку программ, но тоже требует ресурсов.
Насчет линуха, ну дык поддержка нового железа тоже ресурсы и место отнимает, то же автомонтирование например.
Да, но рост требований программного обеспечения к железу просто геометрический)
И это неприятно.
Ну дык и рост требований пользователей к ПО тоже такой же.
Недавно только все смотрели CD рипы, а уже всем нужен блюрей, который весит в десятки раз больше.
Недавно все сидели на квадратных окнах, а теперь всем нужны закругленные окна с прозрачностью и тенями.
Недавно все сидели на статичных страницах, а теперь всем нужен js, гугл мапсы, видео и семантичная верстка. И чтобы сайтов было много, и чтобы они были качественные.
Почему-то никто не хочет монтировать руками флешки, ждать выхода дров месяцами, сидеть на дефолтном опенбоксе и links, а текст редактировать в abiword.
Причем люди не готовы платить за это деньги и ждать годами.
П.С.
Да и насчет 2-3 тяжелых приложений вы тоже лукавите. 10 лет назад чтобы держать открытыми 3d max, photoshop и corel draw одновременно с открытми файлами в них нужна была очень мощная машина.
ну при увеличение картинки видео в 2 раза сложность обработки растет более чем в 2 раза
Да, вот только сд рипы мы смотрели на пентиум4-3 ггц, а бд рипы уже на кореN-3ггц, у которых рост производительности на ядро составил примерно процентов 40%.
Пора уже понять, что рост вычислительной мощи на ядро закончился, а на 8 ядер в одном кристалле с очень медленной общей памятью далеко не все задачи можно раскидать.
Как раз разница примерно в 6 раз идет между VCD и BD, причем это чисто по разрешению, не учитывая то, что на VCD хуже качество видео и звука.
сд рипы можно было на первом пне смотреть :)
был такой плеер досовский, qvpro вроде.
CD рипы и DVD я смотрел на Duron 850 Mhz
Меня всегда удивляют такие комменты. Вы понимаете, что одна страничка хабра весит 500 килобайт? А то, что это все еще надо визуализировать с прозрачностью, в нужном порядке и с динамикой(или вам хочется отвечать на коммент на отдельной странице?), то, что это надо хранить не в виде текста, а в виде дерева, чтобы можно было манипулировать через яваскрипт, тот же жрущий флеш и html5, те же сандбоксы, чтобы браузер не валился целиком а только плагин.
И не забудьте, что это должно работать сегодня, а не завтра. И браузер у вас никто не будет покупать, а будут использовать бесплатно.

На словах умных много, а не деле ближайший аналог тот же хром жрет еще больше.
Обычно не правы обе стороны в категоричных суждениях.
Есть куча говнокода, который жрет ресурсы — это плохие программисты.
Но есть и куча задач, который тоже жрут ресурсы.
Одно дело говнокод, другое дело просто не оптимизированный код, потому что нет времени, ресурсов, возможности, желания. Даже если разработчику лень оптимизировать код какого-то приложения, например kde4, написав неоптимизированный код он уже сделал во много раз больше чем тысяча хомячков возмущающихся насчет этого кода.
ну да, главная хабра 500кб. из них ровно 18кб полезного текста (в уникоде), все остальное — overbloat. 500кб это сравнимо по размерам с войной и миром. И да, тормозит хабра немеряно, в частности из-за флэш банеров макбук разгоняет моторы в подготовке к выходу на третью космическую, а под линуксом отельные статьи скролятся с 2фпс из-за скрипта новых комментов (да, я еще помню как хорошо все работало до его введения). Спасибо, хабра — это классический пример того как не надо делать сайт и как можно испортить хорошее, стремлением к «лучшему».
Ну ведь есть еще разметка, оформление. Если хотите смотреть без них, можно смотреть через тот же elinks. Там и не тормозит.
Кроме хабра есть тот же гмайл, где тоже много чего незаметно на первый взгляд.
«Флэш-баннеры....»

Очнитесь, на дворе XXI век! Уже давно изобретены ад-блокеры ;)
Ковырял я причину тормозов. Называется она mootools. Отключаешь библиотеку, и Хабр летает
>Особенно в линупсе замечал: кде4, гноме, фаерфокс (вообще взрыв мозга), опенофис (!!!!!) жрут всю память что находят и еще в свап зарываются.

Вы, возможно, не поверите, но в линуксе подобное поведение сделано намеренно, и данные с диска по максимуму кэшируются в памяти, сбрасываясь на диск по необходимости. Дадите гиг — заберут гиг, дадите четыре — со временем заберут и четыре. И это правильно, потому что память должна работать (не путать с «память нынче дешёвая»). Если у меня 4 гига — то нафига заставлять систему лишний раз трепать диск, если можно хранить те же данные в оперативе?
Так эта память под файлы не записывается на счёт приложения. Но предыдущий оратор как-то не прав. Например, у меня firefox сейчас жрёт около 120 мегабайт памяти, показывая вот это обсуждение (а оно не хилое такое).

Хотя, openoffice мозг взрывает, конечно, но нужно учитывать, что туда Java ещё намешана.
друг, я сам раз 10 точно объяснял в подробностях разным людям (втч и сотрудникам) что такое память в линуксе и как она работает и почему ее почти никогда нет свободной. Это все легко и расписано школьниками в интернетах. Вот что я не мог объяснить, так это почему система у которой свободно порядка 70% (50-70Гб) оперативки активно уходит в свап вместо того чтоб освобождать кэши. И даже в рэдхате нам ничего не смогли по этому поводу посоветовать.

Речь конечно шла о том что ФФ+ООо+кде4 легко оккупируют 1-1.5Гб оперативки. При том что пару лет назад оно все летало на 256мб.
Планировщики памяти бывают разными. Есть несколько поведений:
1. Выделяем кусок памяти, который максимально подходит под размеры приложения. Фрагментация памяти будет низкой, если возникнет приложение, которое потребует много памяти и сразу, система легко ее выделит
2. Выделяем как можно больший кусок памяти. Приложение может быстро выполниться и освободить все тот же большой кусок свободной памяти.
3. Выделяем константу памяти, например, выделяем приложению всегда 1/10 свободной памяти.
4. Не освобождаем память, если ее дофига свободной.
И еще много-много разных поведений.

Так вот, в некоторых случаях планировщику проще скинуть на винт контекст, чтобы освободить максимум ресурсов для более активного приложения, чтобы повысить отзывчивость системы именно в активом приложении.
Напомню, это поддерживается в основных, стабильных (не альфа\бета) ветках всех основных компиляторов.
Расскажите это Apple, которые до сих пор поставляют gcc 4.2.1.
Я осознаю, что не всё еще в мире гладко и в некоторых ситуациях приходится прогибаться под изменчивый мир. Но они никуда не денутся.
Apple семимильными шагами пилит clang. Год назад я думал, что ждать еще года 2-3. А он уже есть
И тама пока C++0x очень уж странно работает, точнее не работает. Попытался их libc++ заюзать, так он её сам и не увидел. Как ему её скормить?
у меня в xCode смог. Но глубже не копал
Я вообще собирал libc++ в Генте, хотя официально написано, что они её в Линуксе не тестили.
хз. на лялихе мне лень
а вот в портах вчера clang собрался без проблем
Да собирается то он прекрасно, но libc++ не подхватывает. Точнее не видит его iclude'ов
в результате чего
#include
и т.п. не обрабатываются
надо курить. на работе у меня gcc пока что
Есть кстати и другие причины, «почему мы до сих пор это не используем».
Во-первых, менять компилятор, при нашем объеме кода это очень долго и дорого. Мы уже меняли с VS2003 на VS2008 и с gcc 4.0 на 4.2 и знаем не понаслышке.
Во-вторых, не все 3rd-party библиотеки, которые у нас используются, есть для VS2010.
UFO just landed and posted this here
а obj-c++

не стоит думать об ограниченности apple
Вы пробовали выполнить проект на objective-c хотя бы с использованием stl?
Ад и Израиль, вот, что это такое! Выглядит такой код дико.
Приходилось obj-c мешать с C++/Qt
вот и я мешал. Но в ограниченных объемах
Да я тоже, на хакинтоше вдволь не наиграешься.
Не правда. У меня хакинтошь 24*7
И amd проц и видео ati 57xx? И звук 5.1 работает? не-не-не
В огрызке не интересно играться.
ну вот тут нужен большой шаманский бубен. и клок волос джобса.

у меня все железо под хак собиралось
На видео 57хх сейчас нативные драйвера эппл выпустила, а звук 5.1 — да, играет. И вайфай — да, работает. И даже блютуз работает. Правда проц интел.
Вот я о том же — получается месиво из врапперов, адаптеров и костылей.
Ну да, 4.6.1 бета. Жалобы конечно есть (они есть на версии для всех ОС), но люди на нем собирают. Подробнее тут и тут.
Вроде уже не бета… Хотя вчера что-то не собралось. Из-за x86_64

Танцую с бубном
Читаем внимательно начало ветки. Фраза автора, к которой я написал комментарий:
Напомню, это поддерживается в основных, стабильных (не альфа\бета) ветках всех основных компиляторов.


Ну а вообще продакшен код компилировать бетой — моветон.
Насчет беты — это бета 4.6.1, а не 4.6.0. 4.6.0 для дарвина такая же как и у всех остальных.

Я сам на Cях не программирую, просто иногда собираю софт с исходников. Но знакомые говорят, что с 4.5 были проблемы с Obj-C, а с 4.6 вроде все нормально.
Я не знаю, как из портов поставить стейбл:

% port info gcc46
gcc46 @4.6-20110325 (lang)
Variants: gfortran, java, universal

Description: The GNU compiler collection, including front ends for C, C++, Objective-C and Objective-C++. This is a prerelease BETA
version and does not have all available language front ends enabled.


Но даже если бы можно было — врядли бы мы решились на переезд без веских причин.
В портах идет самая последняя, так как беты все лучше и лучше работают на osx, поэтому туда включают самый последний код. И так еще будет как минимум пару месяцев.

А насчет переезда, то тут конечно спешить некуда, но чисто для освоение новых фич и тестирования стабильности, мне кажется, это было бы уместно.
Cтандарты — хорошо. Тут дело скорее в следующем обратная совместимость играет опредленную роль не
всегда есть возможность использовать последний gcc 4.x, в некоторых ообластях например en.wikipedia.org/wiki/Embedded_system… Toolchain «замерз» на «древних компиляторх».
Образно говоря иногда предпочтение отдается даже «C» ввиду всё той же портируемости между платформами. В тоже время можно найти полноценные C++ библиотеки при желании.

Ввиду всё той же обратной совместимости можно компилировать последние версии php ,ruby На Solaris и прочие вещи :) К этому можно добавить что в Юникс Системах например в POSIX стандарте прописано поведение функциий стандартной библиотеки, там также pubs.opengroup.org/onlinepubs/9699919799/.
Есть тесты которыми можно проверить совместимость, есть также дыры в реализации, не понятно что куда вернется при определнных вызовах.

Программы на C/C++ как снежный ком, все используют общие блоки, т.е. если переписать какую то популярную библиотеку на с++ чистый к примеру взять тот же zlib возникнут небольшие проблемы у тех кто ей пользуется не для всех платформ соберется.
UFO just landed and posted this here
1. Цитата: «Все, что я буду дальше писать касается только программирования на С++ и только mainstream-компиляторов (gcc, Intel, Microsoft).»

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

2. Цитата: «Также я буду говорить только о прикладном программировании под десктоп-операционки.»

В купе с п. 1 мы оставляем за бортом огромное поле, на котором ведется разработка на С++.
На счет версий — Википедия в помощь. На счет «оставляем за бортом огромное поле» — согласен. Но я вообще не могу представить себе какое-нибудь утверждение, справедливое на всей области использования языка С++.
К примеру:
Возьмем самую распространенную среду разработки под самую распространенную ОС. С++0х поддерживается пока только в 2010 версии.
Много у вас знакомых, разрабатывающих в последней версии студии?
… особенно в лицензионной
У меня достаточно много.
Но это скорее всего специфика C# направления в разработке.
имелись в виду естественно С++-программисты.
>Много у вас знакомых, разрабатывающих в последней версии студии?
Пару десятков. А зачем писать в НЕ-последней?
Что значит «зачем» если ее тупо нету, например?
Если существует хотя бы одна версия, то существует версия, которая на данный момент является последней.
Бывает платный софт, где за апгрейд версии нужно платить.
Привет, кэп.
«Не закуплено фирмой» и «тупо нету» — разные вещи.
«тупо нету» = «нет в наличии у программиста».
Возьметесь объяснить необходимость покупки последней студии абсолютно всем работодателям?
Я — нет, для этого у МС есть целый штат маркетологов, это их работа. Но могу посоветовать показать работодателям эту статью, которая не «маркетинговый бред», а «разработчикам от разработчиков».
смешно, но нужна статья «работодателям от разработчиков».
Понимаете, переезд «почти готового» проекта на новую версию компилятора, как правило, не лучшая идея. Свои-то баги еще не пофиксили, зачем еще новые вносить?
Поэтому обновление среды обычно происходит между проектами или между мажорными версиями проекта.
А с этим аспектом я и не спорю — это разумно ;)
a << 1; // наверное, опечатка, имелось ввиду a = a << 1;
a <<= 1; // больше нра
Да, конечно, опечатка. Спасибо, исправил.
Большая проблема заключается в том, что в стабильной инфраструктуре используются часто стандартные пакеты для компиляторов, а вот большинство разработчиков не спешат обновлять в своих продуктах данные пакеты.
Сложилось мнение, что кто-то поспешил показать свою непревзойденность.
Может топик и не плох, но стиль изложения угнетает.
Сами написали «Люди имеют завышенную самооценку, считают себя умнее всех», так пускай все сам и решают что для них надо, а что нет. Рано или поздно один заметит проблемы в своем стиле и коде и начнет развиваться самостоятельно, а другой наоборот погрязнет во всех своих недочетах с головой и сменит профессию.
Программист он на то и программист, что умеет самостоятельно находить решение для конкретных задач и грамотно воплощать его в реальность.
Если это был их личный код…
Так а в чем проблема? Значит не надо нанимать на работу не грамотных программистов, ну или по крайней мере доверять им что-то столь важное или серьезное.
Если же наоборот по воле работодателя работаете вместе с такими, то тут либо вы нашли не правильную работу, либо вы на самом деле ни чем не отличаетесь от тех с кем вы работаете.

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

Но он очень хорошо работает, чтоб его переписывать.
Простите, а накой хрен в 21ом веке что-то массово писать на C++?

Нет, я понимаю драйвера на сишечке, бутлоадеры на асме и адскую SIMD-магию в кодеках. Но на кой это в остальных нормальных вещах?

>Если речь идет о количестве людей, предметов, транзакций, температуре, дате, расстоянии, размерах файлов и т.д. — пользуйтесь типами long, longlong или чем-то специализированным. Забудьте о byte, short и int.

За выбор типа данных от балды нужно убивать в первую очередь.
А почему бы не писать? Это не сложно
UFO just landed and posted this here
>драйвера на сишечке, бутлоадеры на асме и адскую SIMD-магию в кодеках.
А еще игры, CAD-системы, видеоредакторы, компиляторы всех остальных языков, операционки, низкоуровневые библиотеки, браузеры… продолжать?
>А еще игры, CAD-системы, видеоредакторы, компиляторы всех остальных языков, операционки, низкоуровневые библиотеки, браузеры… продолжать?

да-да, конечно, в кого не плюнь — все пишут компиляторы, причем на плюсах, а компилируют они исключительно сами себя, а не какой-то прикладной код.
а чего Вы за компиляторы зацепились? Остальных примеров мало? Ну добавьте сюда еще виртуальные машины, микропроцессоры, стеки протоколов, разнообразные DirectX и OpenGL-программы, файловые системы, БИОСы… продолжать?
Знаете, когда десктопный софт тормозит это уныние навевает. Он должен быть максимально быстрым! Это не Ъынтерпрайз, где хочешь не хочешь, а работать на тормозном софте заставим!
Ах да, не забываем про мобилы и планшеты.
Тот же C# и Java позволяют не думать о некоторых вещах, например об удалении памяти. Но если вы пишете серьёзный проект, то вам всё равно рано или поздно придётся об этом думать и возможность контролировать этот ресурс напрямую скорее плюс, чем минус.

Если надо что-то запрограммить на скорую руку просто чтобы работало, то С++ может оказаться не лучшим выбором, это да.
>Тот же C# и Java позволяют не думать о некоторых вещах, например об удалении памяти.

Заблуждение, которое приводит к тому, что софт на джаве памяти жрёт больше, чем С++ софт даже с наличием утечек.
Собственно я об этом и говорю. В шарпе и джаве о контроле над памятью можно забыть и жить спокойно до поры до времени, но это аукнется. Память — это ресурс и управляешь ли ты им в явном виде или неявном, думать о нём всё равно надо.
Ага, в танке! Более менее с++0x в gcc появилось только с выходом 4.6 менее месяца назад (25го марта, а на дворе 12 апреля)
А в msvc тоже не всё гладко, согласно табличке:
blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx
не видно нового for через итерабельные типы, не видно Initializer lists.

В обоих невидно наследуемых/делегируемых конструкторов и Non-static data member initializers (мелочевки которая очень упрощает жизнь)
Вообще, в мире IT, отставать на месяц — это часто почти аналог «быть в танке» :)
На студию Вы зря наехали, 2 неподдерживаемые и 5 частично поддерживаемых фич из 23 — весьма недурно.
Вообще-то менять компилятор, а за ним и дизайн проекта каждый месяц можно только в мыслях.
Только фич этих более 50 если верить табличке gcc :)
Я сам буквально недавно заинтересовался С++0x.

Действительно потрясающе. Но есть одно «но»: поддержка компиляторами.

Если в *никсах все более или менее неплохо — вот в новой убунте уже 4.5 gcc идет по дефолту, а 4.6 можно прикрутить в пару пинков), который поддерживает довольно солидный сабсет С++0х, то с MSVS 2010 все довольно пасмурно. Вот табличка того что он поддерживает. Конечно неплохо, но… А в некоторых других реализациях компиляторов еще менее радужно (например в LLVM C++ и с инициализаторами туго, и с лямбдами).

А так хотелось бы иметь и там возможность писать что-то вроде (компилябельно на gcc 4.6):
vector<pair<string,int>> v = { {"one",1} ,{"two",2},{"three",3} };
for(auto i : v ) { cout<<i.first<<"="<<i.second<<endl; };

(а вот заменить вроде уже на array нельзя, так как std::array уже не может взять свой размер с initializer list. хотя надо попробовать добавить...)
Последней строкой должно быть — и вообще есть более современный язык — C#.

Перешёл с C++ на C#, нравится. Хотя у C++ есть какое своё обаяние…
и про дот.нет не забудьте. И все это на линукс-сервере обрабатывающем запросы
C# — неплохой язык, но всё же ограниченный. Скажем так, не всегда, не везде и не так просто его можно использовать. Возможно, было бы веселее, если бы в Mono была реализация WPF, а не только устаревшего WinForms. Но чего нет — того нет.

C++ меня привлекает ещё и своей универсальностью. Вероятность того, что в реальном проекте встретится задача, не реализуемая на C++ ни под каким соусом, очень невелика. А вопросы быстрой разработки кода (пусть и в ущерб производительности) во многом решаются существующими фреймворками.
Только для Windows

fixed.

в mono далеко не всё так прекрасно.
Как-то слишком много ляпов в статье.
0х это здорово, но с поддержкой пока все очень сложно. Если бы автор реально что-то кодил, он бы это знал.
Код для примеров тоже вызывает улыбку: понятно, что опечататься может каждый, но так возьми любимый компилятор и проверть код на ошибки.
Ну и эпичный финал — взять helloworld из Википедии;) и после этого писать статью в стиля «я тут папко, а вы йуные подаваны»
Буду благодарен за указания на ляпы. И да, я реально пишу с использованием С++0х.
А helloworld — так это вообще фотка наскальной живописи на стене пещеры. Автор, конечно же, не я. Миллион лет рисунку. :)
Ну, так, на вскидку:
а<<1 вам уже указали;
в части про векторы и auto как минимум два ляпа — первый это явная опечатка, второй это несоответствие «старого» и «нового» кода;
MySuperVector очевидно должен быть шаблоном;
Про хелловорлд, пусть даже на картинке, вам уже сказали.
Итого из 6 маленьких демонстрационных кусочков кода у вас проблемы в 4. Многовато для гуру.
И да, на мой взгляд rvalue-ссылки никак не заслуживают упоминания под номером 2 в списке фич 0х, 99.9% не знакомых с ними программистов, увидев ЭТО скажут omfg wtf?
1. Побитовый сдвиг был приведен как пример кода, который плохо читается и понимается и как писать не надо. Я так и не пишу — потому и опечатался. Опечататься в а*=2 намного сложнее. Так что лишняя иллюстрация к тезису вышла.
2. За опечатку в примере векторов спасибо.
3. «MySuperVector очевидно должен быть шаблоном». Конечно должно быть! И еще и по-хорошему, унаследованным от vector или вообще на него замененным. Об том же и речь была, что это НЕВЕРНЫЙ код.
4. Хелловорлд, во-первых — картинка на спине мамонта (что уже навевает серьёзное к ней отношение), во-вторых — из Вики. Да, я пользуюсь Вики при написании статьи. Что в этом плохого?
5. Список не отсортирован по важности или полезности фич, так как эти вещи — повод для холивара.

Спасибо за критику, Вы помогаете статье стать лучше.
Наследоваться от стандартного вектора? Уууу… Спрячьте это немедленно, пока вас не заклевали.
Хочу и наследуюсь. Чем плохо?
Тем, что стандартные контейнеры просто не предназначены для того, чтобы от них наследоваться. В Java они бы имели атрибут final.
У стандартного вектора нет виртуальных функций, причем это сделано сознательно, ради скорости. Это означает, что все функции, принимающие в качестве параметров стандартный вектор, имеют почти все методы стандартного вектора зашитыми глубоко внутрь кода. Если вы передадите в такую функцию свой вектор с перегруженными методами — функция все равно будет использовать методы стандартного вектора и может угробить ваши данные (полиморфизм не включится). Если вы передадите в такую функцию перегруженный вектор с дополнительными полями данных — функция может разрушить эти поля данных или даже произвольную область памяти (потому что она о них ничего не знает, а полиморфизм не включится). Причем эта «опасная» функция может вызываться где-то внутри библиотеки, о чем вы вообще не подозреваете. Поэтому наследование от вектора — это бомба замедленного действия, которая неизвестно когда рванет.
Все, что вы можете сделать безопасно — это добавить к стандартной реализации вектора какие-то свои функции, не имеющие отношения к работе собственно вектора. Но для этого не нужно наследоваться, Си++ позволяет определять функции, в том числе виртуальные, и без этого.
Но действительно правильным решением будет создание обертки вокруг стандартного вектора, то есть класса с private: vector v;.
эти ограничения применимы почти к любым типам без полиморфного поведения
А зачем тогда спрашивать?:)
Затем, что такое ограничение стандартно и о нем надо помнить.

А наследоваться можно, если добавлять поведение, а не менять.
Мужик, искренне жму руку.
Я с самого начала своего пути, как только вывел свое первое hello world на паскале, извечно спорил с людьми, которые напрочь откидали новое и тыкали носом в, чуть ли не бинарный, код и крича, что это основа, это то, что надо и должно быть только так! Согласен, основы знать надо, но застегивать пуговицы через задницу китайскими палочками…

Примерно такое же мнение у меня и с другими языками. Не хочу никого обидеть, я уважаю и одинаково использую другие языки программирования, но когда мне доказывают что это про-родитель и это лучше, что эти новые штуки, которые непонятно как(для него) работают — это все от лукавого и надо все делать ручками… Не понимаю!

Статья на актуальна, на злобу дня =))) Но увы, есть уйма упёртых =(
Пусть пишут. Технологии когда нить победят толпу
Всегда компромисс. Технологий, этапов, ресурсов. Ощущения, подобные вашим, я испытал когда перебрался на Python после C++. Прототип пишется намного быстрей. Просто забываешь о ненужных на данном этапе вещах. Но на production этапе, особенно если алгоритм ресурсоёмкий, выигрыш может быть в сотни и тысячи раз, если написать то же самое на C++. Но если сравнимо или разница небольшая, то да, легче пожертвовать лишними Mb оперативки ради удобства поддержки и времени. Конечно в случае, если пользователь не испытывает от этого никаких неудобств. Задачи всегда разные, как и способы их решения.
Спасибо. Не то, чтобы я этого не знал, но а) сведено воедино и б) сведено блистательно.
Видя во что превращается С++, давно пересел на Си. Жиреете уже от «синтаксического сахара».
И параллели у вас не в тему. Если ваша подруга попросила одолжить у вас машину, пойдете купите новую?
Ну, насчёт отказываться от C++ — не соглашусь. Просто нужно понимать, что для каждой задачи свой язык. И если лучше Си, то лучше его и использовать. Но это не означает, что Си лучше везде и всегда.

А вот насчёт передачи по указателю — тут автор дал маху. Представляю себе потребление стека, если передаваться всё будет по значению, и сколько на этом будет потеряно времени. Ужасно плохой совет. А уж если это не просто структура, а класс…
Ссылки — те же неявные указатели. Автор их уравнял в своём тексте.
В заголовке раздела уже:
Передача всего и везде по указателям (ссылкам)
На самом деле если объекты маленькие, или у них хорошо реализован COW, можно и по значению передавать без проблем. Автор же не советовал отказаться от передачи по указателю/ссылке полностью, он написал
передавать сущности в функции и методы как по ссылке так и по значению — очень мощный механизм и не стоит его использовать однобоко
Почти никогда не требуется передавать по значению, кроме как это совсем маленькая структура. Аргумент — его могут изменить — как-то смотрится мега дико. Я бы даже сказал немного паранойей попахивает. «const_cast» в проекте вообще должен отсутствовать.
Ну вот алгоритмы STL принимают итераторы по значению, а не по константной ссылке.
Я же сказал — «кроме как это совсем маленькая структура». Я же не призываю передавать int по ссылке, когда это не надо? :)
Так автор же не призывает передавать по значению всё и всегда. Он как раз в частности о том и пишет, что маленькие структуры передавать по указателям/ссылкам не всегда оправдано.
Возможно это и имелось в виду, однако получилось у автора в этом пункте несколько иное: забейте на оптимизацию передачи по ссылке, она не важна.
Да и аналогия мягко говоря кривовата. Скорее передача по сслыке это расшарить в сеть папку, а по значению — это копировать на DVD.
Спасибо Paul за защиту, а NickLion за критику. Возможно, я и правда немного не ясно выразился в этом абзаце (но менять уже не буду, люди прочитали и написали свои комментарии). Конечно же, вы оба правы — нужно думать головой и оценивать размеры передаваемых сущностей.
Ладно, уболтали :) Просто, видимо, слишком экспрессивно это было выражено. Наболело, наверное :) А думать головой — это, кстати, универсальный совет.
UFO just landed and posted this here
и в таких случаях надо писать комментарий
Причём коментарий с глубочайшими извинениями перед высшими сущностями, за то что недостаток архтектуры C++ приходится прикрывать такими жуткими костылями :)
Ну, я почти серьёзно. Такие вещи конечно бывают необходимы, но использовать их надо с максимальной осторожностью.
То есть я просто смысла не вижу.
UFO just landed and posted this here
Честно говоря, когда вижу Microsoft'вский C++ CLI, костыли Boost'а или C++0x, впечатление, что смотрю на совершенно другой язык. Может это и к лучшему, эволюция и т.д., но моими глазами: это просто издевательство над языком…
Только когда мне не прийдется при обявлении каждого метода дважды писать его сигнатуру (в хедере и в реализации) я поверю, что 21 век наступил.
А что у нас сигнатура перестала быть идентификатором метода?
ась? вопрос был зачем сигнатуру писать 2 раза.
1й раз прототип в хедере — нафиг он нужен???
2й раз при реализации
ключевое слово — перегрузка

метод/функция идентифицируются по:
* имени
* типу фактических параметров (в том числе с учетом типа this)
и каким образом это отвечает на вопрос «нахрена писать прототип функции?»??

С++ — язык со строгой типизацией. Если компилятор не будет знать прототип функции в месте вызова — он не сможет проверить валидность кода.
Ну вы как дети прямо. C#, Java, AS3 — языки со строгой типизацией и нигде нет прототипов ибо они не нужны.

А компилятор С++ настолько туп, что не может по реализации функции определить ее прототип?
class A {
void f();
void f() const;
};



Теперь напише мне, что вы хотите в файле раелизации. Мне надо написать реализацию 2 функций, каждая из которых имеет тип f
void A::f()
{
}

void A::f() const
{
}
ну вы же полностью указали сигнатуру метода. Разве нет?
Вот именно!!! именно здесь она и нужна. а зачем она нужна в хедере, если я ее уже сдесь указал
А как компилятор определит, какой метод требуется вызывать в точке вызова?
точно также как он это определяет сейчас.

void f();
void f() const; в хедере не добавило ни бита полезной информации
не понял. давайте подробнее.

что будет в хедаре
что будет в реализации
что будет в коде, который использует хеадер
например такой вариант. один из возможных (забудьте тот вариант, что я написал вверху)
\\хедер
class A {
void f()
{
//do some shit
};
void f() const
{
//do some shit
};

реализация пустая
Объявлять символы перед использованием нужно из-за того, что грамматика позволяет неоднозначно трактуемые выражения. Классический пример:
Type foo(A);

Это может быть либо определение функции foo либо создание экземпляра класса Type. Если не заставить объявлять типы перед использованием нельзя будет скомпилировать этот код.

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

Так что боюсь, 21 век для вас никогда не наступит.
И где тут неоднозначность???

Type foo(A) {} — функция

Type foo(A); — переменная.

И никто не предлагал отменять хедеры — это б нарушило обратную совместимость. Пускай исопльзуются в библиотеках, которые поставляются скомпилироваными. Но пускай в новых версиях компиляторов они будут необязательны
Type foo(A); — переменная.
Нет, это forward declaration функции, которая принимает параметр типа A и возвращает объект типа Type.
не понял вашу логику — вы имеете ввиду, что в стандарте С++ уже присутствует неоднозначность и компилятор уже не знает, что делать?
Пропустил комментарий, заметил только сейчас, прошу прощения.

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

Смотрите, не зная, что такое Type и A, невозможно правильно распарсить эту строку.
Если A — это тип, то данная строка это forward declaration функции.
Если A — это объект, то данная строка это создание экземпляра типа Type.

typedef int Type;
typedef short A;
Type foo(A); // forward declaration функции foo

// позже можно сделать
foo(2);


struct Type
{
    Type(int) {}
    void bar() {}
};
const int A = 1;
Type foo(A); // создание экземпляра типа Type

// позже можно сделать
foo.bar();


В хедеры обычно выносятся объявления, а не определения (за исключением определений классов), дабы ускорить процесс компиляции. Можно конечно и определения инклудить, но это будет медленнее.

Можно конечно попытаться написать такой компилятор, который будет сам искать определения без явного указания где их искать, но в этом случае компиляция будет ещё на порядок медленнее и могут начаться проблемы с нарушением ODR.
UFO just landed and posted this here
Вопрос — почему б компилятору не компилировать в 2 прохода?
что важнее — уменьшить работу компилятору или программисту?
компилятор проходит в несколько проходов
не спорю, это был ответ на «Он не может забежать вперед»
Может. Но даже если очень будет пытаться, он не сможет залезть в другую единицу трансляции.
UFO just landed and posted this here
UFO just landed and posted this here
ок. без проблем — где надо юзать хедеры — пускай юзаються. но зачем они обязательны?
я пишу программу, пишу реализацию, у меня весь код, я его меняю и перекомпилирую каждые 5-10мин. при каждом изменении метода я меняю хедер. при каждом новом методе копипейтсю сигнатуру в хедер — зачем я это все делаю?
если реализация класса А зависит от интерфейса класса Б, а реализация класса А зависит от интерфейса класса Б, вы не сможете скомпилировать не тот, не другой, потому что информация о интерфейсе в вашем варианте является результатом компиляции.
от интересно, а как другие компиляторы других языков (например c#) справляються? и без хедеров както
а у них поиск методы может идти в runtime
это тут точно нипричем — если б любой метод в c# линкался в рантайме через рефлекшен, тормоза были б нереальные
он это может сделать во время компиляции байт-кода

фиг знает — я в нем не разбираюсь. но в любом случае они вынуждены использовать данные из других единиц трансляции — а это усложнение
тоесть все изза того, чтоб упростить работу компилятору? может лучше постаратся сделать лучше и упростить работу разработчику
мне не кажется это упрощением
правильный путь: написал интерфейс, пишешь реализацию. А не наоборот.
ок. все Java, C#, Ruby, AS, etc. девелоперы пишут неправильно. Так как реализация класа там одновременно описывает его интерфейс
у них код и реализация пишется вместе.
ruby вооще динамический, как и python
да. у них интерфейс и реализация идут вместе. — вы выше сказали, что это неправильный путь.
вывод: только с++ девелоперы адепты «правильного пути», а все остальные ошибаються.

я правильно понял?
А для четкого отделения интерфейса от реализации используются стандартные ООП практики — интерфейсы, абстрактные классы итд
В C++ сохраняется одно из важных свойств: сборка единицы трансляции не связанно с другими единицами трансляции и библиотеками.
это конечно хорошо. но это не заслуга обьявления каждой сигнатуры по 2 раза.
в том числе и заслуга этого
Чем это свойство так важно? Все равно сборка без наличия библиотек (с одними header-файлами) лишена смысла, т.к. даже тесты запустить не получится.
UFO just landed and posted this here
ага. уберу. компилятор скажет method was not declared in this scope.
UFO just landed and posted this here
Вы можете реализовывать класс прямо в хедере, никто ж не запрещает. Только компилироваться это будет на порядок дольше.
не могу. если буду вызывать метод который обьявлен позже — будет method was not declared in this scope.
class Foo
{
public:
    void foo() { bar(); }
    void bar() { foo(); }
};


Отлично компилится, можете сами проверить.
признаю. был неправ
Ага, а ещё инлайнится будет с большей охотой.
Ну это зависит. Сейчас компиляторы умные пошли, сами инлайнят всё, что под руку попадётся.
С этим бывают странные косяки, когда начинаешь -fvisibility=hidden ставить. С экспортируемыми классами может случиться внезапный косяк при линковке.
Хотелось бы поинтересоваться у автора, зачем, например, писать вот такую дикость:

int count=std::count_if(
ints.begin(),
ints.end(),
boost::bind(
std::logical_and(),
boost::bind(std::greater(), _1, 5),
boost::bind(std::less_equal(), _1, 10)));

Вместо "наскального", уж простите меня, старпера, C-шного "for+if" в три строчки?
зачем вам это здесь надо, не понимаю
А мне этого и не надо.

Покажите мне код, который стал понятнее, быстрее или короче от использования того же boost::bind.

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

stltest.cpp: In function ‘int main()’:
stltest.cpp:9: error: no matching function for call to ‘std::vector<int, std::allocator >::insert(int)’
/usr/lib/gcc/i486-linux-gnu/4.0.3/../../../../include/c++/4.0.3/bits/vector.tcc:93: note: candidates are: typename std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::insert(__gnu_cxx::__normal_iterator<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = int, _Alloc = std::allocator]
/usr/lib/gcc/i486-linux-gnu/4.0.3/../../../../include/c++/4.0.3/bits/stl_vector.h:657: note: void std::vector<_Tp, _Alloc>::insert(__gnu_cxx::__normal_iterator<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, size_t, const _Tp&) [with _Tp = int, _Alloc = std::allocator]

bind: dev.mdc.ru

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

вы конечно можете это все написать ручками, но:
* это будет много кода (в разы больше, чем у меня)
* логика цепочки будет разбросана по самим callback'ам (то есть один должен в конце конов дернуть другой callback)
Неужели вот это:

request.
try_(parse).
catch_( boost::bind(catchFailed, _1, a_authorizeDate.second, a_authorizeDate.first) ).
try_( boost::bind( &CMdcServerConnection::setAuthorized, this_ptr, true ) ).
dtry_( boost::bind(&CMdcServerConnection::selectCurrentCleintId, this_ptr) ).
result();


Чем-то проще или лучше, чем

request.parse();
CMdcServerConnection::setAuthorized(true);
CMdcServerConnection::selectCurrentCleintId();
return request.result();


, обернутое в try-catch?
> dtry_( boost::bind(&CMdcServerConnection::selectCurrentCleintId, this_ptr) ).

Вот это выполняется асинхронно с запросом на сервер.
В этом и главное преимущество: вы даже не заметили, что есть отличие в синхронном и асинхронном коде. буковка d как бы намекает, что deferred. Хотя у меня есть версия, где они не нужны (эти буковки).
И да, вам еще это надо отдать выше. Так что учтите, этот метод тоже используется асинхронным кодом.
это плохой пример моего ребячества. куда более показательно, если dtry идет первым, а потом идет перехват исключения, и тоже асинхронно, а потом еще один dtry
sequence::deferred_result filterModelWithCastedAttr(const CTaskModel& a_model, const QString& a_attr, const ItemId& a_id)
{
return NEW_STARTED_SEQUENCE.
dtry_( boost::bind(&CTaskModel::init, a_model) ).
dtry_( boost::bind(&castToByModelAttr, a_id, a_model, a_attr) ).
try_( boost::bind(buildFilteredModel, a_model, a_attr, _1) ).
result();
}
Кстати, в selectCurrentCleintId — опечатка. :-)

Может я скажу глупость, но какой смысл в асинхронном вызове CMdcServerConnection::selectCurrentCleintId, если мы все равно будем ждать его завершения?
Завершение кого? этого метода? Да, но в нем тоже строится отложенная очередь.

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

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

На самом деле это функция фактически формирует цепочку callback'ов с определенными параметрами, а уж они потом будут выполняться, когда придет время
кстати говоря, request уже есть начало ассинхронной цепочки, так что даже методы parse() и CMdcServerConnection::setAuthorized(true) будут выполнены уже после выполнения request'а
украсть два часа времени на ковыряние в кучах чего-то типа

STLFilt
> Хотелось бы поинтересоваться у автора, зачем, например, писать вот такую дикость

Хоть и не автор, попробую объяснить.

Некоторые C++-программисты вначале пишут в уме код на for-if'ах. Затем улавливают смысл производимых действий, а именно: всё что здесь есть, это всего лишь фильтрация-агрегация. Смотрите, я больше не говорю ни про какие императивные циклы и if-операторы, я мыслю в декларативных терминах проекций, фильтраций, свёрток. Далее C++ программист пишет в уме код на нормальном языке типа OCaml'а, Haskell'а или даже C#, например:
ints.Count(i => 5 < i && i <= 10);
И уже затем с нормального языка переводят на C++, рождая чудовищ.

Ещё раз. Не зная промежуточных рассуждений, кажется, что программист непонятно зачем заменил понятные for-if'ы на заумные binding'и и функторы. На самом же деле, он просто перевёл задачу в удобные для рассуждения термины, и закодировал их в меру убогости C++.

При должной сноровке, опытный программист в приведённом вами коде не увидит всего этого мусора типа boost::bind и std::logical_and. Но не увидит он и for-if'ов. А выцепит только самую суть — фильтрацию-свёртку.
То есть код должен быть понятным только опытному программисту? Ок, понял.
Слишком громкое заявление в заголовке. Я ожидал как минимум, что вы предложите новую идеологию.
Ожидал холивар в статье, а он в комментах.
Кстати, про исключения. Довольно занятно :) Скачал тут llvm просвящаться на тему trueЪ C++. И вот, можно посмотреть:

$ grep -Re 'throw.*;' * | wc -l
187
$ grep -Re 'catch (' * | wc -l
26
$ du -s ./
69608   ./


почти 70 мегабайт кода, и всего 200 точек использования исключений.
лямбда-выражения?
замыкания?

оно синтаксическим сахаром или как?
Ну boost.lambda то написали до С++0x. Выходит синтаксический сахар :)
Я видел народ даже yeld'ы делал при помощи нескольких ассемблерных вставок. В общем то с ними практически всё на свете в сахар превращается.
>>> Специфика программирования 20 лет назад была совсем другой. Счет памяти и ресурсов процессора шел на байты и такты

Счёт на байты и такты шёл сильно раньше (ещё в те времена когда в СССР о программировании мало кто знал) ещё в 82-86 году во всех крупных софтверных компаниях, тактика программирования сменила направление, и все забыли про байты и такты. (Кроме производителей софта для игровых приставок, слабых компов типа C64, автоответчиков, и прочей спец-техники.)

В наше время во многих ВУЗ-ах за рубежом даже не учат что такое такты и байты, дают сразу мануал по С# и по нему штудируют. (У Джоеля можно почитать к чему это приводит.)
Приводит это к тому, что такие люди всё равно нужны, а их днём с огнём не сыщешь!
Про ссылки — а что стоит написать foo(const S & ..)?
Ну а в целом да — если проект позволяет использовать все новомодные фишки, то почему бы их не использовать.
Но не всегда есть такие возможности.
А ничего, но константность можно снять const_cast'ом. Да и в многопоточной среде может случится странное.
В посте про многопоточность не было ни слова — там был аргумент «случайно поменять данные».
Я себе слабо представляю ситуацию, когда программист «случайно» напишет const_cast :)
Если уж пишешь const_cast, то думать надо что делаешь.

А с многопоточностью ни кто не гаранирует, что в момент создания копии структуры, для её передачи в функцию, какое-то из её полей не поменяется в другом потоке — тут как ни крути в каком-то месте синхронизацию межпоточную прикручивать придётся.
Странно. Посмотрите лекции по программированию стенфорда или мит. Будут там и байты и низкоуровневое барахло. И даже, о ужас, будут рассказывать как реализовывать контейнеры. Да, сейчас и память дешевле, и железо лучше. Вот только и информации больше. В некоторых приложениях экономия нескольких бит на структуру может сильно сократить весь объем.
Можно даже добавить, что иногда только экономия нескольких бит позволяет вообще задачу засунуть в оперативную память. Иногда приходится и с миллиардами чисел работать.
Сам некоторое время назад пришёл к идее использования встроенный лямбда-выражений, когда стал сталкиваться с использованием уродских boost-овских связываний функций. Приятно видеть, что я не одинок :)
Пруфлинк не работает :)

Fatal error: Uncaught exception 'Exception' with message 'DateTimeZone::__construct() [<a href='datetimezone.--construct'>datetimezone.--construct</a>]: Unknown or bad timezone ()' in /usr/local/data/www/data-segodnya.ru/index.php:54 Stack trace: #0 /usr/local/data/www/data-segodnya.ru/index.php(54): DateTimeZone->__construct('') #1 {main} thrown in /usr/local/data/www/data-segodnya.ru/index.php on line 54
Капец, Вы специально усиливаете мои и без того сильные сомнения! :)

P.S. У меня работает.

Articles