Разбирая один длинный С-шный исходник, заметил что текст относящийся ко всевозможным проверкам как минимум сопоставим с текстом относящимся к самому алгоритму. Например постоянно встречаются такие конструкции: if (0!=var1) {fn(var1);var1=0} fn(var1);var1=0 tempres=fn(...);if (CONST1!=tempres) {...} else debugprint(..."fn"...); tempres=fn(...);if (CONST1==tempres) {debugprint(..."fn"...);goto cleanup}; tempres=fn(...);if (CONST1==tempres) {debugprint(..."fn"...);return ERR}; r=y(X...);if (CONST1?r) {...} else {dbgprint(...)}; r=y(X...);if (CONST1==r) dbgprint(...) else if (CONST2==r) dbgprint(...); var1=fn(...);ASSERT(var1); ASSERT(var1);fn(...,var1,...); if (lpfn!=0) lpfn(...); и т.д. Пока я вижу только два не очень хороших способа от этого избавиться. Первый это переписать все на язык с хорошей поддержкой макросов, например фасм, тогда вызовы будут оформляться например в виде [['?']var] '=' ['?']fn '(' ['?']arg1 ',' ... ')' [( '=' | '~' )CONST1] [( '=' | '~' )CONST2] ... т.е. "?" перед идентификатором будет генерировать код его проверки, а "опции" после "v=y(x)" будут генерировать код проверки результата. Второй способ - написать дополнительный препроцессор к C. Переписывать пару сотен килобайт кода - удовольствие сомнительное, но и в написание препроцессора дело не то чтобы быстрое. Собственно вопрос, есть ли способы получше, а если нет, то существуют ли подобные готовые решения?
если тебя не интересует размер генерируемого кода - могу хоть сейчас нарисовать дефайны, если интересует - надо чутьчуть подумать и тоже можно нарисовать. сам то уже что на дефайнах наработал?
как я понимаю на дефайнах впринципе невозможно реализовать какието условия, т.е. например в том же фасме можно сделать макрос HL с синтаксисом HL [<var>[<flag>] =] <func>[<flag>] ([ <arg1>[<flag>], <arg2>[<flag>],...])[<flag>] где <flag> ::= -z|-nz|-ez|-enz который будет заменять конструкции "HL var1-z=..." на "if (var1==0) {var1=...}" "HL var1-ez=..." на "if (var1==0) ERROR("Error:var1=0");..." "...lpfunc1-nz..." на "... if (lpfunc1!=0) {...} ..." ... "HL func1(...)-enz" на "... tempvar=func1(...); if (tempvar!=0) ERROR("Error:func1 failed");" ,а в С такого вроде как сделать нельзя.
ну что то вроде #define IWISHYOUREDEAD(var,mode,rest) \ if(!var) if(mode=='z'){var=rest} \ else if(mode=='e'){ERROR("suck up!");rest} ну да, это будет всё кодес, но оптимизатор лишнее вытрет, ибо будет сравнение констант. да и удобно ответвления в дефайны отвести так же, чтоб оптимайзеру работы меньше было. и что тебе мешает просто сделать #define movnz..; #define movz..; #define movze..; #define mov....... ?
Во-первых, нафига столько кодеса для отладочного вывода в релизе? А если он в дебаге, то для отладки это норм. Во-вторых, и это главное, такой код - плохой тон. Для чего были придуманы исключения, интересно? Вот как раз, чтобы код не расползался на проверках и каждая критичная ошибка была локализована в своей функции, а все функции обрамлены в try-catch блок. Тогда всей этой порнографии в коде быть недолжно.
А при чем тут исключения? Когда функция возвращает ошибку никаких исключений обычно не происходит. Да и вообще, где в коде "if (!x) f(x)" исключение? А даже если и исключение будет, чем try-catch короче if? Кроме того, при "ошибке", надо еще и сообщение сформировать, типа "100: if (!x) f(x) else ERR('100:f(x) call rejected')". Насчет кода отладочного вызова в релизе - когда научусь писать релизы без предварительных отладочных версий, выкину все лишние проверки. Только вот например конструкции вида "if (!gSomeGlobalHandle){CloseHandle(gSomeGlobalHandle);gSomeGlobalHandle=NULL};" всеравно никуда не денутся.
GoldFinch Когда функция возвращает ошибку - это значит в ней что-то пошло не так, как должно было быть при нормальных условиях. Допустим, если f() - функция открытия файла, в x - это путь, то окажись путь невалидным, имеет место быть исключительная ситуация и здесь правильнее не возвращать код ошибки, а бросать исключение. Если у тебя программа 1000 строк, то может там и неособо незаметно. Суть в том, что если ты имеешь 10 функций и тебе их надо вызвать последовательно, то тебе придётся ещё быть в курсе всех значений, которые они должны вернуть в случае успеха/неудачи. Т.е. обработка ошибок у тебя вылазит за пределы этих функций. В случае с исключениями, о внутреннем строении функций тебе задумываться не надо, ты знаешь, что в случае ошибки будет сгенерировано такое-то исключение, которое у тебя обрабатывается правильно. Сравни: Обработка кодов ошибки: Код (Text): bool f(/* parameter list */); bool g(/* parameter list */); bool d(/* parameter list */); bool q(/* parameter list */); ... if(!f(/* parameter list */)) { /* освобождение ресурсов */ return 0; } if(!g(/* parameter list */)) { /* освобождение ресурсов */ return 0; } if(!d(/* parameter list */)) { /* освобождение ресурсов */ return 0; } if(!q(/* parameter list */)) { /* освобождение ресурсов */ return 0; } Вариант с исключениями: Код (Text): void f(/* parameter list */); void g(/* parameter list */); void d(/* parameter list */); void q(/* parameter list */); try { f(/* parameter list */); g(/* parameter list */); d(/* parameter list */); q(/* parameter list */); } catch(...) { printf("Unknown exception.\n"); // освобождение ресурсов } return 0; LOL. Сразу весь сорец в консоль выводи, чтобы юзер сам думал где у него там ошибка