"Как отформатировать выводимую строку?" — каждый из нас, я уверен, задавался таким вопросом. Не секрет, что сейчас к старому доброму sprintf'у добавилось множество современных выводов на экран: iostream, stringstream, boost. Про их сравнение хорошо рассказывает известная статья. Printf хорош своими скоростью, простотой и наглядностью, но небезопасен: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами или утилитами типа Lint. Проблема размера буфера решается в ф-ции _snprintf, но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++. Просто взгляите на этот код:
void safeSprintf(char *Dst, int DstSize, char *Format, ...)
{
va_list marker;
va_start(marker, Format);
try
{
_vsnprintf(Dst, DstSize, Format, marker); // Главное действо
}
catch(...)
{
WriteLog("criticals.log", "PARSER Exception in safeSprintf!");
try// сбойную строку с форматированием - в лог
{
char TmpBuf[STDSIZE];
strncpy(TmpBuf, Format, STDSIZE);
TmpBuf[STDSIZE-1] = 0; // т.к. strncpy не добавляет NULL при переполнении
prsReplaceChar(TmpBuf, '%', '_'); // убираем форматирование
WriteLog("criticals.log", TmpBuf);
strcpy(Dst, "ERROR! PARSER Exception in safeSprintf!");
}
catch(...)
{
WriteLog("criticals.log", "PARSER Exception while processing exception!");
}
}// catch
va_end(marker);
}
Механизм исключений убирает главную проблему sprintf — недостаток надежности. Такая редакция ф-ции держит даже конструкции типа
safeSprintf(NULL, 1024*1024, NULL, NULL);
— без выпадений, но с записью сбойных операторов в лог для ошибок!
Станет ли такой safeSprintf новой жизнью для sprintf?
А>Printf хорош своими скоростью, простотой и наглядностью, но небезопасен[/b]: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами
Это как? А>...но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++....
Что это за зверь такой — vsnprintf?
str будет передан по значению, т.е. скопируется на стек. Дамп объекта будет расценен как указатель на строку (%s). Допустим, указатель на начало строки содержится в string со смещением 0. Но, поскольку sizeof(string)>sizeof(char*), второй раз мы точно подхватим нечто из первого объекта, указывающее куда-то на мусор.
И с большой вероятностью, получим не AV (с исключением и т.д. по тексту) а просто кучу трухи в выводе.
> "Как отформатировать выводимую строку?" — каждый из нас, я уверен, задавался таким вопросом. Не секрет, что сейчас к старому доброму sprintf'у добавилось множество современных выводов на экран: iostream, stringstream, boost. Про их сравнение хорошо рассказывает известная статья. Printf хорош своими скоростью, простотой и наглядностью, но небезопасен: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами или утилитами типа Lint. Проблема размера буфера решается в ф-ции _snprintf, но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++. Просто взгляите на этот код:
C++ исключения никак не решают проблему невалидных указателей.
Разыменование невалидного указателя может быть причиной платформенного исключения (ms win) или сигнала (posix). Последние, очевидно, не поймать в catch(...). Первые также никак не должны ловиться в сatch(...). То, что VC6 ловит ACCESS_VIOLATION в сatch(...) является ошибкой, которую исправили в VС7 (если я не ошибаюсь), который по-умолчанию не ловит SEH исключения в сatch(...).
Здравствуйте, Vamp, Вы писали:
А>>Printf хорош своими скоростью, простотой и наглядностью, но небезопасен[/b]: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами V>Это как? А>>...но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++.... V>Что это за зверь такой — vsnprintf?
Это такая функция, которая выполняет действия аналогичные snprintf, но принимает параметры в стиле va_list. Пришла она кажись из xНИКСА, поэтому не знаю — стандартная она или нет.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
К>str будет передан по значению, т.е. скопируется на стек. Дамп объекта будет расценен как указатель на строку (%s). Допустим, указатель на начало строки содержится в string со смещением 0. Но, поскольку sizeof(string)>sizeof(char*), второй раз мы точно подхватим нечто из первого объекта, указывающее куда-то на мусор. К>И с большой вероятностью, получим не AV (с исключением и т.д. по тексту) а просто кучу трухи в выводе.
Точно — есть такая буква в слове #$%. В одном проекте использовали sprintf`ы и string`и (это теперь я знаю, какой это геморой, а тогда был молодой и зелёный, поэтому согласился), так на эту граблю вставали раз 10 и главное, что ничего вразумительного придумать не смогли, чтобы этого избежать...
Вобщем, о други, скажу я вам, sprinf и иже с ним — это кал, геморой и очень плохо. Кстати с их помощью ещё и кучу ошибок, можно нагородить, которые потом можно эксплуатировать в атаках через переполнения буфера (см. Лебланк М., Ховард М. Защищенный код).
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, MaximE, Вы писали:
>> "Как отформатировать выводимую строку?" — каждый из нас, я уверен, задавался таким вопросом. Не секрет, что сейчас к старому доброму sprintf'у добавилось множество современных выводов на экран: iostream, stringstream, boost. Про их сравнение хорошо рассказывает известная статья. Printf хорош своими скоростью, простотой и наглядностью, но небезопасен: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами или утилитами типа Lint. Проблема размера буфера решается в ф-ции _snprintf, но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++. Просто взгляите на этот код:
ME>C++ исключения никак не решают проблему невалидных указателей.
ME>Разыменование невалидного указателя может быть причиной платформенного исключения (ms win) или сигнала (posix). Последние, очевидно, не поймать в catch(...). Первые также никак не должны ловиться в сatch(...). То, что VC6 ловит ACCESS_VIOLATION в сatch(...) является ошибкой, которую исправили в VС7 (если я не ошибаюсь), который по-умолчанию не ловит SEH исключения в сatch(...).
ME>-- ME>Maxim Yegorushkin
Более того, в MS VC без компиляции с ключом /EHa компилятор вообще вот этот код:
> str будет передан по значению, т.е. скопируется на стек. Дамп объекта будет расценен как указатель на строку (%s). Допустим, указатель на начало строки содержится в string со смещением 0. Но, поскольку sizeof(string)>sizeof(char*), второй раз мы точно подхватим нечто из первого объекта, указывающее куда-то на мусор. > И с большой вероятностью, получим не AV (с исключением и т.д. по тексту) а просто кучу трухи в выводе.
Про эту проблему автор постинга написал, что она решена тем, что современные компиляторы и Lint выдают предупреждение при передаче объектов в многоточие и также проверкой соответствия типов аргументов *printf типам в строке формата.
> _vsnprintf(Dst, DstSize, Format, marker); // Главное действо
>
> > Потому как _vsnprintf не содержит спецификации исключений и вообще по всем признакам (с точки зрения компилятора) исключения выкидывать не должен.
"все признаки" — это то, что функции объявлены как extern "C". Компиляторы предполагают, что функции с С-линковкой не бросают исключений (потому что исключений нет в С).
АФАИК не POD типы вообще нельзя передавать в функции с переменным числом параметров в качестве дополнительных параметров.
По идее компилятор на таком коде должен вякнуть что-то.
E>А вот у меня на Philips Nexperia почему-то нет SEH.
E>Доктор, что мне делать?
Вопрос, конечно, интересный
А, собственно, какое отношение имеют SEH исключения к исключениям в стиле C++ ?
Если под Windows C++ исключения реализованы (по крайней мере компилятором от MS) через SEH, никто не мешает остальным сделать это по-другому.
Но это мы отвлеклись от главной темы...
IMHO, так называемый safePrintf может быть использован в качестве временной заплатки для определённого сочетания платформы/компилятора при портировании старого кода.
Здравствуйте, adontz, Вы писали:
A>АФАИК не POD типы вообще нельзя передавать в функции с переменным числом параметров в качестве дополнительных параметров. A>По идее компилятор на таком коде должен вякнуть что-то.
Надо будет стандарт посмотреть тщательно.
Однако, VC6 прекрасно принимает классы.
А какая, кстати, разница, POD или не POD? Нетривиальный конструктор/деструктор? Всё равно, удалением объектов со стека будет заниматься вызывающая сторона, а она знает реальные типы.
Здравствуйте, Кодт, Вы писали:
К>Надо будет стандарт посмотреть тщательно. К>Однако, VC6 прекрасно принимает классы. К>А какая, кстати, разница, POD или не POD? Нетривиальный конструктор/деструктор? Всё равно, удалением объектов со стека будет заниматься вызывающая сторона, а она знает реальные типы.
5.2.2 Function call
6 A function can be declared to accept fewer arguments (by declaring default arguments (8.3.6)) or more
arguments (by using the ellipsis,... 8.3.5) than the number of parameters in the function definition (8.4).
[Note:this implies that, except where the ellipsis (...) is used, a parameter is available for each argument.
]
7 When there is no parameter for a given argument, the argument is passed in such a way that the receiving
function can obtain the value of the argument by invokingva_arg (18.7). The lvaluetorvalue (4.1),
arraytopointer (4.2), and functiontopointer (4.3) standard conversions are performed on the argument
expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer,
pointer to member, or class type, the program is illformed. If the argument has a nonPOD class type
(clause 9), the behavior is undefined. If the argument has integral or enumeration type that is subject to the
integral promotions (4.5), or a floating point type that is subject to the floating point promotion (4.6), the
value of the argument is converted to the promoted type before the call. These promotions are referred to
Так что вякать должен! Но VC7.1 в этом смысле тоже молчит
Здравствуйте, MaximE, Вы писали:
ME>Mr. None wrote:
ME>[]
>> Более того, в MS VC без компиляции с ключом /EHa компилятор вообще вот этот код:
ME>По-умолчанию в седьмых студиях синхронная модель исключений (/EHs), при которых SEH исключения C++ кодом (catch(...)) не ловятся.
По умолчанию /EHsс, и SEH исключения прекрасно ловятся троеточием с нормальной раскруткой стека. Как, впрочем, и Интелом.
Буковка c означает, что extern "C" функции рассматриваются как не выбрасывающие исключений.
Если эту опцию убрать, то исключения по-прежнему будут хорошо ловится, только без раскрутки стека.
Есть ещё одна опция /EHa (/EHac). Ведет она себя на первый взгляд также.
В чем разница. В том, что в модели синхроных исключений компилятор может выбросить блок try/catch, если сочтет, что в нем нет мест, способных бросить исключения.
При этом места, потенциально опасные с точки зрения SEH-исключений он игнорирует. Вот поэтому и не ловится иногда -- просто потому, что компилятор выбросил соответствующую ловушку.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Кодт, Вы писали:
К>>Надо будет стандарт посмотреть тщательно. К>>Однако, VC6 прекрасно принимает классы. К>>А какая, кстати, разница, POD или не POD? Нетривиальный конструктор/деструктор? Всё равно, удалением объектов со стека будет заниматься вызывающая сторона, а она знает реальные типы.
A>
A>5.2.2 Function call
A>6 A function can be declared to accept fewer arguments (by declaring default arguments (8.3.6)) or more
A> arguments (by using the ellipsis,... 8.3.5) than the number of parameters in the function definition (8.4).
A> [Note:this implies that, except where the ellipsis (...) is used, a parameter is available for each argument.
A> ]
A>7 When there is no parameter for a given argument, the argument is passed in such a way that the receiving
A> function can obtain the value of the argument by invokingva_arg (18.7). The lvaluetorvalue (4.1),
A> arraytopointer (4.2), and functiontopointer (4.3) standard conversions are performed on the argument
A> expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer,
A> pointer to member, or class type, the program is illformed. If the argument has a nonPOD class type
A> (clause 9), the behavior is undefined. If the argument has integral or enumeration type that is subject to the
A> integral promotions (4.5), or a floating point type that is subject to the floating point promotion (4.6), the
A> value of the argument is converted to the promoted type before the call. These promotions are referred to
A>Так что вякать должен! Но VC7.1 в этом смысле тоже молчит
Undefine не значит, что работать не будет. Значит, что работать будет не известно как. Но в конкретной системе вполне это может отработать и корректно.
Здравствуйте, Шахтер, Вы писали:
Ш>По умолчанию /EHsс, и SEH исключения прекрасно ловятся троеточием с нормальной раскруткой стека. Как, впрочем, и Интелом. Ш>Буковка c означает, что extern "C" функции рассматриваются как не выбрасывающие исключений. Ш>Если эту опцию убрать, то исключения по-прежнему будут хорошо ловится, только без раскрутки стека.
Здесь похоже перепутано или я не так понял.
Если объявить inv в твоем примере как extern "C", то с ключем /EHs как раз выполняется раскрутка стека. А с ключем /EHsc — не выполняется (аналогично тому, как если бы inv была объявлена как throw() ).
Ш>Есть ещё одна опция /EHa (/EHac). Ведет она себя на первый взгляд также.
Ш>В чем разница. В том, что в модели синхроных исключений компилятор может выбросить блок try/catch, если сочтет, что в нем нет мест, способных бросить исключения. Ш>При этом места, потенциально опасные с точки зрения SEH-исключений он игнорирует. Вот поэтому и не ловится иногда -- просто потому, что компилятор выбросил соответствующую ловушку.
Ш>
Шахтер wrote:
>>> Более того, в MS VC без компиляции с ключом /EHa компилятор вообще вот этот код: > > ME>По-умолчанию в седьмых студиях синхронная модель исключений (/EHs), при которых SEH исключения C++ кодом (catch(...)) не ловятся. > > По умолчанию /EHsс, и SEH исключения прекрасно ловятся троеточием с нормальной раскруткой стека. Как, впрочем, и Интелом.
Ok, значит я принял ближайшее будущее за настоящую действительность.
The current plan is for the next release not to catch SEH exceptions when
building /EHs (which is already likely broken depending on what the
optimizer decides to do) but to continue doing so when compiled /EHa.
Ronald Laeremans
Visual C++ team
Так что на текущих майкросовтовских компиляторах мы можем пофиксить catch(...) так:
void __cdecl turn_off_se_handler(unsigned int, EXCEPTION_POINTERS*)
{
throw;
}
int main()
{
_set_se_translator(turn_off_se_handler);
try
{
int* p = 0;
*p = 0;
}
catch(...)
{
// never get here
}
}
ME>The current plan is for the next release not to catch SEH exceptions when
ME>building /EHs (which is already likely broken depending on what the
ME>optimizer decides to do) but to continue doing so when compiled /EHa.
ME>Ronald Laeremans
ME>Visual C++ team
Выглядит разумно.
ME>Так что на текущих майкросовтовских компиляторах мы можем пофиксить catch(...) так: