Как бросить const char * exception?
От: Аноним  
Дата: 13.03.13 09:49
Оценка: -1
При возникновении нештатной ситуации хочу сформировать строку с описанием ошибки и передать ее как аргумент в исключение. Как правильно это сделать?

void foo()
{
   char exMsg[100];   

   // ... 
   sprintf( exMsg, "error: %s", err_descr );
   throw exMsg;
}


Вопросы.
1. Mожно ли так делать (передавать указатель на локальный буфер)?
2. Не нравится необходимость иметь массив на стеке, тем более заранее неизвестен размер. Как сделать кошерно?
Re: Как бросить const char * exception?
От: uzhas Ниоткуда  
Дата: 13.03.13 09:50
Оценка: 4 (1) +2 -3
Здравствуйте, Аноним, Вы писали:

А>Как сделать кошерно?


char exMsg[100];   
// ... 
sprintf( exMsg, "error: %s", err_descr );
throw std::exception(exMsg);
Re[2]: Как бросить const char * exception?
От: pkl  
Дата: 13.03.13 10:11
Оценка: +1
Здравствуйте, uzhas, Вы писали:

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


А>>Как сделать кошерно?


U>
U>char exMsg[100];   
U>// ... 
U>sprintf( exMsg, "error: %s", err_descr );
U>throw std::exception(exMsg);
U>


А разве объявленный автоматический char exMsg[100] не перестанет существовать после разматывания стека в ходе проброса исключения? Конечно, стек будет только разматываться, в него уже ничего не будет записано и лежащая в нём строка останется скорее всего невредимой, но выгдядит это по-хакерски.
Re[2]: Как бросить const char * exception?
От: saf_e  
Дата: 13.03.13 10:47
Оценка:
Здравствуйте, uzhas, Вы писали:

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


А>>Как сделать кошерно?


U>
U>char exMsg[100];   
U>// ... 
U>sprintf( exMsg, "error: %s", err_descr );
U>throw std::exception(exMsg);
U>


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

Поэтому тут выход только один хранит строку в объекте исключения, например использую std::runtime_error.
Re[3]: Как бросить const char * exception?
От: okman Беларусь https://searchinform.ru/
Дата: 13.03.13 11:03
Оценка: +2 -3
Здравствуйте, pkl, Вы писали:

char exMsg[100];   
// ... 
sprintf( exMsg, "error: %s", err_descr );
throw std::exception(exMsg);


pkl>А разве объявленный автоматический char exMsg[100] не перестанет существовать после разматывания стека


Перестанет. Но до того, как это произойдет, строка будет скопирована в экземпляр std::exception.
Поэтому такой код в этом плане полностью безопасен.
Re: Как бросить const char * exception?
От: PM  
Дата: 13.03.13 11:26
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>При возникновении нештатной ситуации хочу сформировать строку с описанием ошибки и передать ее как аргумент в исключение. Как правильно это сделать?


А>
А>void foo()
А>{
А>   char exMsg[100];   

А>   // ... 
А>   sprintf( exMsg, "error: %s", err_descr );
А>   throw exMsg;
А>}
А>


А>Вопросы.

А>1. Mожно ли так делать (передавать указатель на локальный буфер)?
А>2. Не нравится необходимость иметь массив на стеке, тем более заранее неизвестен размер. Как сделать кошерно?

0. В отличие от других языков, С++ позволят бросаться любым типом исключений, но практичнее использовать традиционный подход — бросать исключения, унаследованные из иерархии std::exception.

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

2. Если использовать массив фиксированного размера и sprintf("%s"), то рано или поздно получишь переполнение буфера (повезет, если рано и получишь ты, а не взломщик сервера). Для форматирования использовать std::stringstream, boost::format и т.п. В простейшем случае может хватить std::string:

void foo()
{
   if ( some_failure )
   {
       std::string err_msg = std::string("error: ") + err_descr;
       throw std::runtime_error(err_msg); // или еще какой-нибудь потомок std::exception
   }
}
Re[2]: Как бросить const char * exception?
От: jyuyjiyuijyu  
Дата: 13.03.13 11:28
Оценка:
Здравствуйте, uzhas, Вы писали:

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


А>>Как сделать кошерно?


U>
U>char exMsg[100];   
U>// ... 
U>sprintf( exMsg, "error: %s", err_descr );
U>throw std::exception(exMsg);
U>


так вроде безопаснее... хотя если не хватит памяти...

U>
U>throw ( boost::format( "error: %s" ) % err_descr ) .str().c_str();
U>
Re[4]: Как бросить const char * exception?
От: Evgeny.Panasyuk Россия  
Дата: 13.03.13 11:29
Оценка: +2
Здравствуйте, okman, Вы писали:

u>>throw std::exception(exMsg);

O>Перестанет. Но до того, как это произойдет, строка будет скопирована в экземпляр std::exception.
O>Поэтому такой код в этом плане полностью безопасен.

MSVC — не единственный компилятор
C++03, 18.6.1
namespace std {
    class exception {
    public:
        exception() throw();
        exception(const exception&) throw();
        exception& operator=(const exception&) throw();
        virtual ~exception() throw();
        virtual const char* what() const throw();
    };
}
Re[4]: Как бросить const char * exception?
От: PM  
Дата: 13.03.13 11:37
Оценка: +1
Здравствуйте, okman, Вы писали:

O>Здравствуйте, pkl, Вы писали:


O>
O>char exMsg[100];   
O>// ... 
O>sprintf( exMsg, "error: %s", err_descr );
O>throw std::exception(exMsg);
O>


pkl>>А разве объявленный автоматический char exMsg[100] не перестанет существовать после разматывания стека


O>Перестанет. Но до того, как это произойдет, строка будет скопирована в экземпляр std::exception.

O>Поэтому такой код в этом плане полностью безопасен.

Вообще-то у std::exception нет конструктора, принимающего char* или std::string.

Код с использованием sprintf("%s") не может считаться безопасным.
Re[5]: Как бросить const char * exception?
От: okman Беларусь https://searchinform.ru/
Дата: 13.03.13 11:50
Оценка: +3
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>MSVC — не единственный компилятор


Ок, заменим std::exception на std::runtime_error.
Re[5]: Как бросить const char * exception?
От: okman Беларусь https://searchinform.ru/
Дата: 13.03.13 11:52
Оценка: -1
Здравствуйте, PM, Вы писали:

PM>Код с использованием sprintf("%s") не может считаться безопасным.


pkl>>А разве объявленный автоматический char exMsg[100] не перестанет существовать после разматывания стека

O>Перестанет. Но до того, как это произойдет, строка будет скопирована в экземпляр std::exception.
O>Поэтому такой код в этом плане полностью безопасен.

Re[5]: Как бросить const char * exception?
От: uzhas Ниоткуда  
Дата: 13.03.13 12:20
Оценка: :)))
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>MSVC — не единственный компилятор

студия схавала мой мозг
Re: Как бросить const char * exception?
От: Alexéy Sudachén Чили  
Дата: 13.03.13 12:44
Оценка:
А>2. Не нравится необходимость иметь массив на стеке, тем более заранее неизвестен размер. Как сделать кошерно?

Если без помощи асфальтоукладочного катка, то где-то так:

#include <stdarg.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <sstream>
#include <iomanip>

std::string sformat(char *fmt, ...) {
    va_list va;
    va_start(va,fmt);
    std::vector<char> buf(_vscprintf(fmt,va)+1);
    vsprintf(&buf[0],fmt,va);
    va_end(va);    
    return std::string(&buf[0]);
}

int fa() {
    throw std::runtime_error( sformat("fa adderss %p",fa) );
}

int fb() {
    throw sformat("fb adderss %p",fb);
}

int fc() {
    std::stringstream ss; 
    ss << "fc adderss " << std::hex << fc;
    throw /*std::runtime_error(*/ ss.str() /*)*/;
}

int main()
{
    try { 
        fc();
    }catch(std::string &e) { 
        printf("error:%s",e.c_str());
    }catch(std::exception &e) { 
        printf("error:%s",e.what());
    } 
}


Выбирай что больше нравится.
Re[3]: Как бросить const char * exception?
От: Erop Россия  
Дата: 14.03.13 13:03
Оценка:
Здравствуйте, saf_e, Вы писали:

_>Поэтому тут выход только один хранит строку в объекте исключения, например использую std::runtime_error.


Ещё можно в статический std::string, например, сложить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Как бросить const char * exception?
От: wander  
Дата: 14.03.13 17:19
Оценка:
Здравствуйте, jyuyjiyuijyu, Вы писали:

U>>
U>>throw ( boost::format( "error: %s" ) % err_descr ) .str().c_str();
U>>


Временный объект std::string, c_str() у него, потом разрушение std::string — бамс!, в catch прилетает невалидный пойнтер.
Знал бы ты сколько такой херни мне пришлось видеть и исправлять. Не делайте так. Пожалуйста.
объект std::string
Re[4]: Как бросить const char * exception?
От: saf_e  
Дата: 14.03.13 17:58
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, saf_e, Вы писали:


_>>Поэтому тут выход только один хранит строку в объекте исключения, например использую std::runtime_error.


E>Ещё можно в статический std::string, например, сложить...


Ну статический не подойдет, а вот per-thread уже вполне
Re[2]: Как бросить const char * exception?
От: enji  
Дата: 16.03.13 07:18
Оценка:
Здравствуйте, Alexéy Sudachén, Вы писали:

AS>

AS>std::string sformat(char *fmt, ...) {
AS>    va_list va;
AS>    va_start(va,fmt);
AS>    std::vector<char> buf(_vscprintf(fmt,va)+1);
AS>    vsprintf(&buf[0],fmt,va);
AS>    va_end(va);    
AS>    return std::string(&buf[0]);
AS>}

AS>

А зачем этот изврат с форматированием?
Взять boost::format или stringstream. Исключения кидаются редко, экономить на форматировании смысла нет
Re[3]: Как бросить const char * exception?
От: Alexéy Sudachén Чили  
Дата: 16.03.13 12:59
Оценка:
E>А зачем этот изврат с форматированием?
E>Взять boost::format или stringstream. Исключения кидаются редко, экономить на форматировании смысла нет

stringstream в примере есть, но он патологически неудобен, а что до сисек ... не все знаете ли любят тяжёлые наркотики, некоторые предпочитают лёгкие )))
Re[4]: Как бросить const char * exception?
От: enji  
Дата: 16.03.13 19:03
Оценка:
Здравствуйте, Alexéy Sudachén, Вы писали:

E>>А зачем этот изврат с форматированием?

E>>Взять boost::format или stringstream. Исключения кидаются редко, экономить на форматировании смысла нет

AS>stringstream в примере есть, но он патологически неудобен, а что до сисек ... не все знаете ли любят тяжёлые наркотики, некоторые предпочитают лёгкие )))


что такое сиськи? Новое название буста?

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

stringstream кстати для исключений довольно удобен, ИМХО. Неудобен он, когда нужно что-то форматировать (манипуляторы — ужас). А когда надо просто загнать какую-то инфу в строку и на форматирование плевать (эксепшены, логирование) — поудобнее принтфа. А если использовать свой stringstream, отвязанный от локали — еще и быстрее
Re[5]: Как бросить const char * exception?
От: Alexéy Sudachén Чили  
Дата: 16.03.13 20:06
Оценка:
AS>>stringstream в примере есть, но он патологически неудобен, а что до сисек ... не все знаете ли любят тяжёлые наркотики, некоторые предпочитают лёгкие )))
E>что такое сиськи? Новое название буста?

Очень даже старое )))

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


То мне об этом скажет компилятор ))) И в весьма читабельном виде.

#include <stdio.h>

struct A {
   char S[10];
};

struct A a = { "hello" };

int main() {
    printf("%s world\n",a);
}


cl: warning C6284: Object passed as parameter '2' when string is required in call to 'printf'
gcc: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'struct A'

а чего там скажет форматтер сисек после чайной паузы на время компиляции?
#include <boost/format.hpp>
using namespace std;

struct A { 
   char a[10]; 
};

A a = { "hello" };

int main() {
   cout << boost::format("%s world") % a << endl;
}


  Скрытый текст
boost/format/feed_args.hpp(100) : error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const A' (or there is no acceptable conversion)
        include\ostream(679): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(726): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,char)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(764): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(811): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,char)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(937): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const signed char *)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(944): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,signed char)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(951): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const unsigned char *)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(958): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,unsigned char)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(968): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>,T>(std::basic_ostream<_Elem,_Traits> &&,_Ty)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>,
            T=A,
            _Ty=A
        ]
        include\ostream(1085): or       'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const std::error_code &)' [found using argument-dependent lookup]
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        boost/format/group.hpp(45): or       'std::basic_ostream<_Elem,_Traits> &boost::io::detail::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const boost::io::detail::group0 &)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(186): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(std::basic_ostream<_Elem,_Traits> &(__cdecl *)(std::basic_ostream<_Elem,_Traits> &))'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(192): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(std::basic_ios<_Elem,_Traits> &(__cdecl *)(std::basic_ios<_Elem,_Traits> &))'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(199): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(std::ios_base &(__cdecl *)(std::ios_base &))'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(206): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(std::_Bool)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(226): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(short)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(260): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(unsigned short)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(280): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(int)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(305): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(unsigned int)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(325): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(long)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(345): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(unsigned long)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(366): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(__int64)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(386): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(unsigned __int64)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(407): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(float)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(427): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(double)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(447): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(long double)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(467): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(const void *)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        include\ostream(487): or       'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(std::basic_streambuf<_Elem,_Traits> *)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        while trying to match the argument list '(std::basic_ostream<_Elem,_Traits>, const A)'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        boost/format/feed_args.hpp(159) : see reference to function template instantiation 'void boost::io::detail::put_last<char,std::char_traits<char>,T>(std::basic_ostream<_Elem,_Traits> &,const T &)' being compiled
        with
        [
            T=A,
            _Elem=char,
            _Traits=std::char_traits<char>
        ]
        boost/format/feed_args.hpp(254) : see reference to function template instantiation 'void boost::io::detail::put<Ch,Tr,Alloc,T>(T,const boost::io::detail::format_item<Ch,Tr,Alloc> &,std::basic_string<_Elem,_Traits,_Ax> &,boost::io::basic_altstringbuf<Ch,Tr,Alloc> &,boost::io::detail::locale_t *)' being compiled
        with
        [
            Ch=char,
            Tr=std::char_traits<char>,
            Alloc=std::allocator<char>,
            T=const A &,
            _Elem=char,
            _Traits=std::char_traits<char>,
            _Ax=std::allocator<char>
        ]
        boost/format/feed_args.hpp(263) : see reference to function template instantiation 'void boost::io::detail::distribute<Ch,Tr,Alloc,T>(boost::basic_format<Ch> &,T)' being compiled
        with
        [
            Ch=char,
            Tr=std::char_traits<char>,
            Alloc=std::allocator<char>,
            T=const A &
        ]
        boost/format/format_class.hpp(64) : see reference to function template instantiation 'boost::basic_format<Ch> &boost::io::detail::feed<char,Tr,Alloc,const T&>(boost::basic_format<Ch> &,const A)' being compiled
        with
        [
            Ch=char,
            Tr=std::char_traits<char>,
            Alloc=std::allocator<char>,
            T=A
        ]
        3.cpp(9) : see reference to function template instantiation 'boost::basic_format<Ch> &boost::basic_format<Ch>::operator %<A>(const T &)' being compiled
        with
        [
            Ch=char,
            T=A
        ]

Типа такая вот демонстрация KISS в жизни.
Re: Как бросить const char * exception?
От: ak239  
Дата: 17.03.13 08:36
Оценка:
Здравствуйте, Аноним, Вы писали:

А>1. Mожно ли так делать (передавать указатель на локальный буфер)?

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

А>2. Не нравится необходимость иметь массив на стеке, тем более заранее неизвестен размер. Как сделать кошерно?

Вообще, лучший вариант — создать наследника своего стандартного исключения и работать с ним ( к примеру, std::runtime_error).
Если очень хочется именно строку — std::string
А если char*, то std::shared_ptr<char*> — это гарантирует, что динамически созданная строка в любом случае, будет уничтожена.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.