sprintf против всех...
От: Аноним  
Дата: 06.09.04 08:41
Оценка: -2
"Как отформатировать выводимую строку?" — каждый из нас, я уверен, задавался таким вопросом. Не секрет, что сейчас к старому доброму 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?
Re: sprintf против всех...
От: Vamp Россия  
Дата: 06.09.04 08:46
Оценка:
А>Printf хорош своими скоростью, простотой и наглядностью, но небезопасен[/b]: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами
Это как?
А>...но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++....
Что это за зверь такой — vsnprintf?
Да здравствует мыло душистое и веревка пушистая.
Re: sprintf против всех...
От: Кодт Россия  
Дата: 06.09.04 09:21
Оценка: 20 (1)
Здравствуйте, Аноним, Вы писали:

А>Станет ли такой safeSprintf новой жизнью для sprintf?


Нет, не станет.
std::string str = "Hello, world!";

char buf[1000];
safeSprintf(buf, 1000, "1: %s\n2: %s\n", str /*1*/, str /*2*/);

str будет передан по значению, т.е. скопируется на стек. Дамп объекта будет расценен как указатель на строку (%s). Допустим, указатель на начало строки содержится в string со смещением 0. Но, поскольку sizeof(string)>sizeof(char*), второй раз мы точно подхватим нечто из первого объекта, указывающее куда-то на мусор.
И с большой вероятностью, получим не AV (с исключением и т.д. по тексту) а просто кучу трухи в выводе.
Перекуём баги на фичи!
Re: sprintf против всех...
От: MaximE Великобритания  
Дата: 06.09.04 10:01
Оценка:
> "Как отформатировать выводимую строку?" — каждый из нас, я уверен, задавался таким вопросом. Не секрет, что сейчас к старому доброму sprintf'у добавилось множество современных выводов на экран: iostream, stringstream, boost. Про их сравнение хорошо рассказывает известная статья. Printf хорош своими скоростью, простотой и наглядностью, но небезопасен: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами или утилитами типа Lint. Проблема размера буфера решается в ф-ции _snprintf, но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++. Просто взгляите на этот код:

C++ исключения никак не решают проблему невалидных указателей.

Разыменование невалидного указателя может быть причиной платформенного исключения (ms win) или сигнала (posix). Последние, очевидно, не поймать в catch(...). Первые также никак не должны ловиться в сatch(...). То, что VC6 ловит ACCESS_VIOLATION в сatch(...) является ошибкой, которую исправили в VС7 (если я не ошибаюсь), который по-умолчанию не ловит SEH исключения в сatch(...).

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 beta
Re[2]: sprintf против всех...
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 06.09.04 10:24
Оценка:
Здравствуйте, Vamp, Вы писали:

А>>Printf хорош своими скоростью, простотой и наглядностью, но небезопасен[/b]: нет ни проверки типов аргументов, ни размера буфера. Правда, первая проблема уже решена современными компиляторами

V>Это как?
А>>...но даже она не может предотвратить, скажем, передачу некорректных указателей. Но решение есть! Это — исключения, штатный механизм C++....
V>Что это за зверь такой — vsnprintf?

Это такая функция, которая выполняет действия аналогичные snprintf, но принимает параметры в стиле va_list. Пришла она кажись из xНИКСА, поэтому не знаю — стандартная она или нет.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: sprintf против всех...
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 06.09.04 10:39
Оценка: +2 :))
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Аноним, Вы писали:


А>>Станет ли такой safeSprintf новой жизнью для sprintf?


К>Нет, не станет.

К>
К>std::string str = "Hello, world!";

К>char buf[1000];
К>safeSprintf(buf, 1000, "1: %s\n2: %s\n", str /*1*/, str /*2*/);
К>

К>str будет передан по значению, т.е. скопируется на стек. Дамп объекта будет расценен как указатель на строку (%s). Допустим, указатель на начало строки содержится в string со смещением 0. Но, поскольку sizeof(string)>sizeof(char*), второй раз мы точно подхватим нечто из первого объекта, указывающее куда-то на мусор.
К>И с большой вероятностью, получим не AV (с исключением и т.д. по тексту) а просто кучу трухи в выводе.

Точно — есть такая буква в слове #$%. В одном проекте использовали sprintf`ы и string`и (это теперь я знаю, какой это геморой, а тогда был молодой и зелёный, поэтому согласился), так на эту граблю вставали раз 10 и главное, что ничего вразумительного придумать не смогли, чтобы этого избежать...
Вобщем, о други, скажу я вам, sprinf и иже с ним — это кал, геморой и очень плохо. Кстати с их помощью ещё и кучу ошибок, можно нагородить, которые потом можно эксплуатировать в атаках через переполнения буфера (см. Лебланк М., Ховард М. Защищенный код).
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: sprintf против всех...
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 06.09.04 10:45
Оценка: +1
Здравствуйте, 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 компилятор вообще вот этот код:
try
{
   _vsnprintf(Dst, DstSize, Format, marker); // Главное действо
}
catch(...)
{
// ...
}


Соптимизирует к такому:
_vsnprintf(Dst, DstSize, Format, marker); // Главное действо


Потому как _vsnprintf не содержит спецификации исключений и вообще по всем признакам (с точки зрения компилятора) исключения выкидывать не должен.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: sprintf против всех...
От: MaximE Великобритания  
Дата: 06.09.04 11:12
Оценка:
Кодт wrote:

[]

> Нет, не станет.

>
> std::string str = "Hello, world!";
>
> char buf[1000];
> safeSprintf(buf, 1000, "1: %s\n2: %s\n", str /*1*/, str /*2*/);
>

> str будет передан по значению, т.е. скопируется на стек. Дамп объекта будет расценен как указатель на строку (%s). Допустим, указатель на начало строки содержится в string со смещением 0. Но, поскольку sizeof(string)>sizeof(char*), второй раз мы точно подхватим нечто из первого объекта, указывающее куда-то на мусор.
> И с большой вероятностью, получим не AV (с исключением и т.д. по тексту) а просто кучу трухи в выводе.

Про эту проблему автор постинга написал, что она решена тем, что современные компиляторы и Lint выдают предупреждение при передаче объектов в многоточие и также проверкой соответствия типов аргументов *printf типам в строке формата.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 beta
Re: sprintf против всех...
От: elcste  
Дата: 06.09.04 11:20
Оценка:
Здравствуйте, Аноним, Вы писали:

А>   try
А>   {
А>      _vsnprintf(Dst, DstSize, Format, marker); // Главное действо
А>   }
А>   catch(...)
А>   {

А вот у меня на Philips Nexperia почему-то нет SEH.

Доктор, что мне делать?
Re[3]: sprintf против всех...
От: MaximE Великобритания  
Дата: 06.09.04 11:25
Оценка:
Mr. None wrote:

[]

> Более того, в MS VC без компиляции с ключом /EHa компилятор вообще вот этот код:


По-умолчанию в седьмых студиях синхронная модель исключений (/EHs), при которых SEH исключения C++ кодом (catch(...)) не ловятся.

>
> try
> {
>    _vsnprintf(Dst, DstSize, Format, marker); // Главное действо
> }
> catch(...)
> {
> // ...
> }
>

>
> Соптимизирует к такому:
>
> _vsnprintf(Dst, DstSize, Format, marker); // Главное действо
>

>
> Потому как _vsnprintf не содержит спецификации исключений и вообще по всем признакам (с точки зрения компилятора) исключения выкидывать не должен.

"все признаки" — это то, что функции объявлены как extern "C". Компиляторы предполагают, что функции с С-линковкой не бросают исключений (потому что исключений нет в С).

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 beta
Re[2]: sprintf против всех...
От: adontz Грузия http://adontz.wordpress.com/
Дата: 06.09.04 12:12
Оценка:
Здравствуйте, Кодт, Вы писали:

К>
К>std::string str = "Hello, world!";

К>char buf[1000];
К>safeSprintf(buf, 1000, "1: %s\n2: %s\n", str /*1*/, str /*2*/);
К>


АФАИК не POD типы вообще нельзя передавать в функции с переменным числом параметров в качестве дополнительных параметров.
По идее компилятор на таком коде должен вякнуть что-то.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: sprintf против всех...
От: Тигра Беларусь  
Дата: 06.09.04 12:43
Оценка:
Здравствуйте, elcste, Вы писали:

E>
А>>   try
А>>   {
А>>      _vsnprintf(Dst, DstSize, Format, marker); // Главное действо
А>>   }
А>>   catch(...)
А>>   {
E>

E>А вот у меня на Philips Nexperia почему-то нет SEH.

E>Доктор, что мне делать?

Вопрос, конечно, интересный

А, собственно, какое отношение имеют SEH исключения к исключениям в стиле C++ ?
Если под Windows C++ исключения реализованы (по крайней мере компилятором от MS) через SEH, никто не мешает остальным сделать это по-другому.

Но это мы отвлеклись от главной темы...

IMHO, так называемый safePrintf может быть использован в качестве временной заплатки для определённого сочетания платформы/компилятора при портировании старого кода.
Re: sprintf против всех...
От: e-Xecutor Россия  
Дата: 06.09.04 12:52
Оценка:
Здравствуйте, Аноним, Вы писали:

Так, соображения на тему:
#include <stdio.h>
#include <string>
#include <sstream>
#include <list>
#include <vector>
#include <new>
#include "Timer.hpp"
#include "boost/format.hpp"
#include <sstream>


using namespace std;

class FormatResult{
public:
  enum{FORMAT_RESULT_BUFFER_SIZE=128};
  FormatResult():length(0),size(FORMAT_RESULT_BUFFER_SIZE),inHeap(false),ptr(buf)
  {
    buf[0]=0;
  }
  ~FormatResult()
  {
    if(inHeap)delete [] ptr;
  }
  FormatResult(const FormatResult& src)
  {
    length=src.length;
    if(src.length+1>FORMAT_RESULT_BUFFER_SIZE)
    {
      inHeap=true;
      size=length+1;
      ptr=new char[size];
    }else
    {
      inHeap=false;
      size=FORMAT_RESULT_BUFFER_SIZE;
      ptr=buf;
    }
    memcpy(ptr,src.ptr,src.length);
    ptr[length]=0;
  }
  void Append(const char* str)
  {
    Append(str,strlen(str));
  }
  void Append(const char* str,int len)
  {
    if(length+len+1>size)
    {
      size=(length+len+1)*2;
      char* newptr=new char[size];
      memcpy(newptr,ptr,length);
      if(inHeap)delete [] ptr;
      inHeap=true;
    }
    memcpy(ptr+length,str,len);
    length+=len;
    ptr[length]=0;
  }
  const char* Str()const
  {
    return ptr;
  }
  int Length()const
  {
    return length;
  }
protected:
  char buf[FORMAT_RESULT_BUFFER_SIZE];
  char *ptr;
  int length;
  int size;
  bool inHeap;
};

FormatResult& operator<<(FormatResult& str,int i)
{
  char buf[32];
  int l=sprintf(buf,"%d",i);
  str.Append(buf,l);
  return str;
}

FormatResult& operator<<(FormatResult& str,long double f)
{
  char buf[128];
  int l=_snprintf(buf,sizeof(buf),"%lf",f);
  if(l<0)
  {
    l=sizeof(buf)-1;
    buf[l]=0;
  }
  str.Append(buf,l);
  return str;
}

FormatResult& operator<<(FormatResult& str,double f)
{
  char buf[128];
  int l=_snprintf(buf,sizeof(buf),"%lf",f);
  if(l<0)
  {
    l=sizeof(buf)-1;
    buf[l]=0;
  }
  str.Append(buf,l);
  return str;
}

FormatResult& operator<<(FormatResult& str,float f)
{
  char buf[128];
  int l=_snprintf(buf,sizeof(buf),"%f",f);
  if(l<0)
  {
    l=sizeof(buf)-1;
    buf[l]=0;
  }
  str.Append(buf,l);
  return str;
}

FormatResult& operator<<(FormatResult& str,char c)
{
  str.Append(&c,1);
  return str;
}


FormatResult& operator<<(FormatResult& str,const char* sptr)
{
  str.Append(sptr);
  return str;
}

FormatResult& operator<<(FormatResult& str,const void* ptr)
{
  char buf[32];
  int l=_snprintf(buf,sizeof(buf),"%p",ptr);
  if(l<0)
  {
    l=sizeof(buf)-1;
    buf[l]=0;
  }
  str.Append(buf,l);
  return str;
}

class Format{
public:
  Format(const char* argFmt)
  {
    ParseFormat(argFmt);
  }

  struct ArgList{
    const Format& fmt;
    enum{STACK_ARG_LIST_SIZE=8};
    ArgList(const Format& argFmt):fmt(argFmt),argArrayCount(0),useList(false)
    {
      if(fmt.fmtList.size()>STACK_ARG_LIST_SIZE)
      {
        argList.reserve(fmt.fmtList.size());
      }
    }

    struct SelfFormat{
      virtual void Fmt(FormatResult& out,int w,int p)=0;
    };
    template <class T>
    struct SelfFormatImpl:SelfFormat{
      const T& t;
      SelfFormatImpl(const T& arg):t(arg){}
      virtual void Fmt(FormatResult& out,int w,int p)
      {
        out<<t;
      }
    };

    struct SFHolder{
      union{
        char buf[sizeof(SelfFormatImpl<int>)];
        int  pad;
      };
      SFHolder(){}
      template <class T>
      SFHolder(const T& t)
      {
        new(buf)SelfFormatImpl<T>(t);
      }
      SFHolder(const SFHolder& src)
      {
        memcpy(buf,src.buf,sizeof(buf));
      }
      SelfFormat* get()
      {
        return (SelfFormat*)buf;
      }
    };

    vector<SFHolder> argList;
    SFHolder argArray[STACK_ARG_LIST_SIZE];
    int argArrayCount;
    bool useList;

    template <class T>
    ArgList& operator,(const T& t)
    {
      if(useList)
      {
        argList.push_back(SFHolder(t));
      }else
      {
        if(argArrayCount==STACK_ARG_LIST_SIZE)
        {
          argList.assign(argArray,argArray+argArrayCount);
          argList.push_back(SFHolder(t));
          useList=true;
        }else
        {
          argArray[argArrayCount]=SFHolder(t);
          argArrayCount++;
        }
      }

      return *this;
    }

    FormatResult Fmt()
    {
      FormatResult res;
      for(FmtList::const_iterator it=fmt.fmtList.begin();it!=fmt.fmtList.end();it++)
      {
        if(it->type==FmtItem::fiStatic)
        {
          res.Append(it->str.c_str(),it->str.length());
        }
        else if(it->type==FmtItem::fiFormat)
        {
          int idx=it->idx;
          if(useList)
          {
            if(idx>=0 && idx<argList.size())
            {
              argList[idx].get()->Fmt(res,0,0);
            }
          }else
          {
            if(idx>=0 && idx<argArrayCount)
            {
              argArray[idx].get()->Fmt(res,0,0);
            }
          }
        }
      }
      return res;
    }
  };

  template <class T>
  friend Format::ArgList operator%(const Format& fmt,const T& arg)
  {
    Format::ArgList al(fmt);
    al,arg;
    return al;
  }

protected:
  friend struct ArgList;
  struct FmtItem{
    enum {fiStatic,fiFormat} type;
    FmtItem(const char* s,int len):str(s,len),type(fiStatic){}
    FmtItem(const string& s):str(s),type(fiStatic){}
    explicit FmtItem(int i,int w=-1,int p=-1):type(fiFormat),idx(i),wid(w),prec(p){}
    string str;
    int idx,wid,prec;
  };
  typedef list<FmtItem> FmtList;
  FmtList fmtList;
  void ParseFormat(const char* fmt)
  {
    const char* str=fmt;
    const char* ptr=str;
    while(*str)
    {
      if(*str=='%')
      {
        if(str!=ptr)
        {
          fmtList.push_back(FmtItem(ptr,str-ptr));
        }
        str++;
        if(*str=='%')
        {
          if(fmtList.size()>0)
          {
            if(fmtList.back().type==FmtItem::fiStatic)
            {
              fmtList.back().str+='%';
              str++;
              ptr=str;
              continue;
            }
          }
          fmtList.push_back(FmtItem("%",1));
          str++;
          ptr=str;
          continue;
        }
        int idx=atoi(str);
        idx--;
        fmtList.push_back(FmtItem(idx));
        while(isdigit(*str))str++;
        ptr=str;
        continue;
      }
      str++;
    }
  }
};

int main(int argc,char* argv[])
{
  const int N=1000000;
  {
    int l=0;

    Format fmt("hello %1 world %2 %3:%4");
    printf("%s\n",(fmt % 1 , "test",'!',5.333).Fmt().Str());
    TIMETHIS("myformat",N)
    {
      const FormatResult& s=(fmt % 1 , "test",'!',5.333).Fmt();
      l+=s.Length();
    }
    printf("l=%d\n",l);
  }
  {
    int l=0;
    boost::format fmt("hello %1% world %2% %3%:%4%");
    printf("%s\n",(fmt%1%"test"%'!'%5.333).str().c_str());
    TIMETHIS("boost::format",N)
    {
      const string& s=(fmt%1%"test"%'!'%5.333).str();
      l+=s.length();
    }
    printf("l=%d\n",l);
  }
  {
    int l=0;
    {
      stringstream str;
      str<<"hello "<<1<<" world "<<"test"<<" "<<'!'<<':'<<5.333;
      printf("%s\n",str.str().c_str());
    }
    TIMETHIS("stringstream",N)
    {
      stringstream str;
      str<<"hello "<<1<<" world "<<"test"<<" "<<'!'<<':'<<5.333;
      const string& s=str.str();
      l+=s.length();
    }
    printf("l=%d\n",l);
  }
  {
    int l=0;
    {
      char buf[256];
      _snprintf(buf,sizeof(buf),"hello %d world %s %c:%lf",1,"test",'!',5.333);
    }
    TIMETHIS("sprintf",N)
    {
      char buf[256];
      l+=_snprintf(buf,sizeof(buf),"hello %d world %s %c:%lf",1,"test",'!',5.333);
    }
    printf("l=%d\n",l);
  }
  return 0;
}



Timer.hpp:
#ifndef __TIMER_HPP__
#define __TIMER_HPP__

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>

class Timer{
public:
  Timer()
  {
    start.QuadPart=0;
    QueryPerformanceFrequency(&freq);
  }
  void Start()
  {
    QueryPerformanceCounter(&start);
  }
  void Finish()
  {
    LARGE_INTEGER end;
    QueryPerformanceCounter(&end);
    start.QuadPart=(end.QuadPart-start.QuadPart)*1000/freq.QuadPart;
  }
  __int64 Get()
  {
    return start.QuadPart;
  }
protected:
  LARGE_INTEGER start,freq;
};

class TimeThis{
public:
  TimeThis(const char* argMsg,int argCount):msg(argMsg),count(argCount)
  {
    t.Start();
  }
  operator bool(){return false;}
  ~TimeThis()
  {
    t.Finish();
    int ms=t.Get();
    printf("%s:time=%dms,speed=%lf/sec\n",msg,ms,double(count*1000.0/ms));
  }
protected:
  const char* msg;
  int count;
  Timer t;
};

#define TIMETHIS(msg,n) if(TimeThis tt=TimeThis(msg,n));else for(int i=0;i<n;i++)

#endif
Re[3]: sprintf против всех...
От: Кодт Россия  
Дата: 06.09.04 14:02
Оценка:
Здравствуйте, adontz, Вы писали:

A>АФАИК не POD типы вообще нельзя передавать в функции с переменным числом параметров в качестве дополнительных параметров.

A>По идее компилятор на таком коде должен вякнуть что-то.

Надо будет стандарт посмотреть тщательно.
Однако, VC6 прекрасно принимает классы.
А какая, кстати, разница, POD или не POD? Нетривиальный конструктор/деструктор? Всё равно, удалением объектов со стека будет заниматься вызывающая сторона, а она знает реальные типы.
Перекуём баги на фичи!
Re[4]: sprintf против всех...
От: adontz Грузия http://adontz.wordpress.com/
Дата: 06.09.04 14:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Надо будет стандарт посмотреть тщательно.

К>Однако, 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 lvalue­to­rvalue (4.1),
array­to­pointer (4.2), and function­to­pointer (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 ill­formed. If the argument has a non­POD 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 в этом смысле тоже молчит
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: sprintf против всех...
От: Шахтер Интернет  
Дата: 06.09.04 18:56
Оценка: 8 (1) +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-исключений он игнорирует. Вот поэтому и не ловится иногда -- просто потому, что компилятор выбросил соответствующую ловушку.

/* main.cpp */ 

#include <iostream>

using namespace std;

int inv(int x);

struct Test
 {
  ~Test() { cout << "Test::~Test()" << endl ; }
 };

/* main() */ 

int main()
 {
  try
    {
     Test test;
     
     inv(0);
    }
  catch(...)  
    {
     cout << "Exception" << endl ;
    }
 
  return 0;
 }


/* test1.cpp */ 

int inv(int x)
 {
  return 1/x;
 }
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[5]: sprintf против всех...
От: Шахтер Интернет  
Дата: 06.09.04 18:56
Оценка:
Здравствуйте, 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 lvalue­to­rvalue (4.1),
A> array­to­pointer (4.2), and function­to­pointer (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 ill­formed. If the argument has a non­POD 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 не значит, что работать не будет. Значит, что работать будет не известно как. Но в конкретной системе вполне это может отработать и корректно.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[5]: sprintf против всех...
От: folk Россия  
Дата: 06.09.04 23:47
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>По умолчанию /EHsс, и SEH исключения прекрасно ловятся троеточием с нормальной раскруткой стека. Как, впрочем, и Интелом.

Ш>Буковка c означает, что extern "C" функции рассматриваются как не выбрасывающие исключений.
Ш>Если эту опцию убрать, то исключения по-прежнему будут хорошо ловится, только без раскрутки стека.

Здесь похоже перепутано или я не так понял.
Если объявить inv в твоем примере как extern "C", то с ключем /EHs как раз выполняется раскрутка стека. А с ключем /EHsc — не выполняется (аналогично тому, как если бы inv была объявлена как throw() ).

Ш>Есть ещё одна опция /EHa (/EHac). Ведет она себя на первый взгляд также.


Ш>В чем разница. В том, что в модели синхроных исключений компилятор может выбросить блок try/catch, если сочтет, что в нем нет мест, способных бросить исключения.

Ш>При этом места, потенциально опасные с точки зрения SEH-исключений он игнорирует. Вот поэтому и не ловится иногда -- просто потому, что компилятор выбросил соответствующую ловушку.

Ш>
Ш>/* main.cpp */ 

Ш>#include <iostream>

Ш>using namespace std;

Ш>int inv(int x);

Ш>struct Test
Ш> {
Ш>  ~Test() { cout << "Test::~Test()" << endl ; }
Ш> };

Ш>/* main() */ 

Ш>int main()
Ш> {
Ш>  try
Ш>    {
Ш>     Test test;
     
Ш>     inv(0);
Ш>    }
Ш>  catch(...)  
Ш>    {
Ш>     cout << "Exception" << endl ;
Ш>    }
 
Ш>  return 0;
Ш> }
Ш>


Ш>
Ш>/* test1.cpp */ 

Ш>int inv(int x)
Ш> {
Ш>  return 1/x;
Ш> }
Ш>
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[5]: sprintf против всех...
От: MaximE Великобритания  
Дата: 07.09.04 06:29
Оценка: 4 (1)
Шахтер wrote:

>>> Более того, в MS VC без компиляции с ключом /EHa компилятор вообще вот этот код:

>
> ME>По-умолчанию в седьмых студиях синхронная модель исключений (/EHs), при которых SEH исключения C++ кодом (catch(...)) не ловятся.
>
> По умолчанию /EHsс, и SEH исключения прекрасно ловятся троеточием с нормальной раскруткой стека. Как, впрочем, и Интелом.

Ok, значит я принял ближайшее будущее за настоящую действительность.

using catch(...) in VC++ is cause of unreliable behavior.

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
     }
}


--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9 beta
Re[6]: sprintf против всех...
От: Шахтер Интернет  
Дата: 07.09.04 16:32
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>

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(...) так:


Не совсем понимаю, почему -- пофиксить?
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.