throw, wchar_t* и память
От: __kain Россия  
Дата: 14.04.08 08:28
Оценка:
Добрый день!

Вот и сбылся со мной анекдот — сообщение об ошибке вызвало ошибку

В двух словах: генерирую исключение, хочу посмотреть текст. Вылетаю. Вот код:
#include <windows.h>
#include <stdio.h>
#include <conio.h>

class CSomeException
{
private:
    wchar_t*    _msg;
    int            _code;

public:

    CSomeException(int code, const wchar_t* msg)
    {
        size_t len = wcslen(msg);
        _msg = new wchar_t[len + 1];
        wcscpy(_msg, msg);

        _msg[len] = L'\0';

        _code = code;
    }

    wchar_t* GetMessage()
    {
        return _msg;
    }

    int GetCode()
    {
        return _code;
    }

    ~CSomeException()
    {
        delete _msg;
    }
};

int main()
{
    try
    {
        throw (CSomeException(0, L"Error string"));
    }
    catch(CSomeException& exc)
    {
        MessageBox(0, exc.GetMessage(), L"", 0);
    }
    //
    puts("End...");
    _getch();
    return 0;
}
Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает. Помогите
Re: throw, wchar_t* и память
От: Smal Россия  
Дата: 14.04.08 08:34
Оценка: +1
Здравствуйте, __kain, Вы писали:

__>Добрый день!


__>Вот и сбылся со мной анекдот — сообщение об ошибке вызвало ошибку


А конструктор копирования? (и оператор = тоже неплохо бы)
С уважением, Александр
Re[2]: throw, wchar_t* и память
От: Smal Россия  
Дата: 14.04.08 08:36
Оценка:
Здравствуйте, Smal, Вы писали:

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


__>>Добрый день!


__>>Вот и сбылся со мной анекдот — сообщение об ошибке вызвало ошибку


S>А конструктор копирования? (и оператор = тоже неплохо бы)


Про мораль запамятовал сказать...

Мораль: используй std::wstring
С уважением, Александр
Re: throw, wchar_t* и память
От: Bell Россия  
Дата: 14.04.08 08:36
Оценка: +1
Здравствуйте, __kain, Вы писали:

А конструктор копирования Пушкин писать будет?

ЗЫ
До кучи можно реализовать и оператор присваивания, но он использоваться не должен — попробуй просто добавить его объявление в private-секцию.
Любите книгу — источник знаний (с) М.Горький
Re: throw, wchar_t* и память
От: jazzer Россия Skype: enerjazzer
Дата: 14.04.08 08:50
Оценка:
Здравствуйте, __kain, Вы писали:

__>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает. Помогите


Подумай на тему, какие члены класса генерируются компилятором по умолчанию и как именно они работают.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: throw, wchar_t* и память
От: alzt  
Дата: 14.04.08 09:38
Оценка: 9 (1)
Здравствуйте, __kain, Вы писали:

Извиняюсь, что не разобрался в коде, бросилось в глаза следующее:
delete _msg;


вместо
delete[] _msg;
Re[2]: throw, wchar_t* и память
От: Bell Россия  
Дата: 14.04.08 09:56
Оценка: +1
Здравствуйте, alzt, Вы писали:

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


A>Извиняюсь, что не разобрался в коде, бросилось в глаза следующее:

A>
A>delete _msg;
A>


A>вместо

A>
A>delete[] _msg;
A>


Да, все остальный упустили этот факт...

Но в данном случае причина не в этом, хотя, безусловно, это действительно ошибка. Насколько можно судить по коду — автор вопроса использует компилятор от MS, а он прощает использование delete вместо delete[] для встроенных типов.
Любите книгу — источник знаний (с) М.Горький
Re: throw, wchar_t* и память
От: remark Россия http://www.1024cores.net/
Дата: 14.04.08 10:01
Оценка: +1
Здравствуйте, __kain, Вы писали:

__>Вот и сбылся со мной анекдот — сообщение об ошибке вызвало ошибку

__>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает.


Правило Большой Тройки:

Если ты реализуешь либо конструктор копирования, либо оператор присваивания, либо деструктор, то скорее всего тебе надо реализовать их все три.


Лучше это вшить себе в ПЗУ, если этого там ещё нет



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: throw, wchar_t* и память
От: remark Россия http://www.1024cores.net/
Дата: 14.04.08 10:09
Оценка:
Здравствуйте, __kain, Вы писали:

__>В двух словах: генерирую исключение, хочу посмотреть текст. Вылетаю. Вот код:

__>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает.


Если нельзя использовать std::wstring, то можно так:

class CSomeException
{
private:
    shared_ptr<wchar_t const> msg_;
    int code_;

public:
    CSomeException(int code, const wchar_t* msg)
        : code_(code)
        , msg_(wcsdup(msg), free)
    {
    }

    wchar_t const* GetMessage() const
    {
        return msg_.get();
    }

    int GetCode() const
    {
        return code_;
    }
};



Но вообще с std::wstring было бы оптимально.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: throw, wchar_t* и память
От: __kain Россия  
Дата: 14.04.08 10:31
Оценка:
Всем спасибо! Конструктор копирования спас человечество! Добавил три строчки:
CSomeException(CSomeException& other)
{
    CSomeException(other._code, other._msg);
}
И оно стало работать!

Я теперь понял, что происходит при передаче по ссылке: вызывается конструктор копирования Тогда понятно, почему глюк был. Я раньше думал, что если пишу "&", то "волшебным" образом передается в точности тот объект, который я ему передал! Как если бы я его использовал в месте вызова.

wstring можно использовать, и boost, но мне было важно разобраться, в чем причина моей ошибки.

ВСЕМ ОГРОМНОЕ СПАСИБО! Плюсы с меня

P.S.
Про delete[] я знаю, просто пробовал по-разному запустить, думал именно он влияет на ошибку.
Re[3]: throw, wchar_t* и память
От: Bell Россия  
Дата: 14.04.08 10:46
Оценка: +1
Здравствуйте, __kain, Вы писали:

__>Всем спасибо! Конструктор копирования спас человечество! Добавил три строчки:

__>
__>CSomeException(CSomeException& other)
__>{
__>    CSomeException(other._code, other._msg);
__>}
__>
И оно стало работать!


Ух ты! И с какой версии VC позволяет вызывать конструктор из другого конструктора?
Дело в том, что в стандартном С++ так делать нельзя.

__>Я теперь понял, что происходит при передаче по ссылке: вызывается конструктор копирования

Это заблуждение. Дело не в передаче по ссылке, а в том, как именно создается объект — исключения. А создается он как копия объекта, указанного в throw (здесь
Автор: Bell
Дата: 02.12.03
). Так что ссылка в catch совершенно не при чем.

__>Тогда понятно, почему глюк был. Я раньше думал, что если пишу "&", то "волшебным" образом передается в точности тот объект, который я ему передал! Как если бы я его использовал в месте вызова.


Это так и есть. В этом плане ссылка аналогична указателю.

__>wstring можно использовать, и boost, но мне было важно разобраться, в чем причина моей ошибки.
Любите книгу — источник знаний (с) М.Горький
Re[4]: throw, wchar_t* и память
От: __kain Россия  
Дата: 14.04.08 10:54
Оценка:
Здравствуйте, Bell, Вы писали:

B>Ух ты! И с какой версии VC позволяет вызывать конструктор из другого конструктора?

B>Дело в том, что в стандартном С++ так делать нельзя.
Всего лишь вариант реализации. Запускал на VC2005.

B>Это заблуждение. Дело не в передаче по ссылке, а в том, как именно создается объект — исключения. А создается он как копия объекта, указанного в throw (здесь
Автор: Bell
Дата: 02.12.03
). Так что ссылка в catch совершенно не при чем.


B>Это так и есть. В этом плане ссылка аналогична указателю.

+1
Re: throw, wchar_t* и память
От: jazzer Россия Skype: enerjazzer
Дата: 14.04.08 11:06
Оценка:
Здравствуйте, __kain, Вы писали:

__>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает.


std::wstring в классе исключения — плохая идея, потому что его конструктор может бросить исключение
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: throw, wchar_t* и память
От: Smal Россия  
Дата: 14.04.08 11:08
Оценка:
Здравствуйте, jazzer, Вы писали:

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


__>>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает.


J>std::wstring в классе исключения — плохая идея, потому что его конструктор может бросить исключение


Ага, когда кончилась память . Но тут уж тебя врядли что-то спасет.
С уважением, Александр
Re[3]: throw, wchar_t* и память
От: jazzer Россия Skype: enerjazzer
Дата: 14.04.08 11:14
Оценка:
Здравствуйте, Smal, Вы писали:

J>>std::wstring в классе исключения — плохая идея, потому что его конструктор может бросить исключение


S>Ага, когда кончилась память . Но тут уж тебя врядли что-то спасет.


У тебя есть возможность корректно завершить приложение (хотя бы путем корректного проброса исключения на самый верх), чтобы отработали все деструкторы, сохранились все файлы и т.д.
А это всегда лучше, чем terminate.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: throw, wchar_t* и память
От: remark Россия http://www.1024cores.net/
Дата: 14.04.08 15:26
Оценка:
Здравствуйте, jazzer, Вы писали:

S>>Ага, когда кончилась память . Но тут уж тебя врядли что-то спасет.


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

J>А это всегда лучше, чем terminate.


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

std::exception в msvc++ при нехватке памяти тупо выдаёт строку "Unknown exception".

Уважаемый г-н MaximE предпочитает (если более точно — предпочитал, но в прошедшем времени как-то плохо получается) делать следующим образом. В класс исключения добавляем встроенный буфер некого фиксированного размера, сообщение всегда храним в этом буфере. Пока стек не кончится мы в танке. В смысле не теряем пользовательское сообщение.

Так же разумным выглядит следующий подход. Разрешить такую форму вызова:
  throw my_exception("Some static message", my_exception::dont_copy_and_deallocate);

Т.к. сообщения часто бывают именно статическими. А если оно не статическое, то всё равно есть большая вероятность, что bad_alloc вылетит ещё при формировании сообщения.
Это приём тоже используется в std::exception в msvc++. Это даже доступно для пользователя: std::exception ex ("Some static message", 1);

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

Кто что думает?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: throw, wchar_t* и память
От: alsemm Россия  
Дата: 15.04.08 06:42
Оценка:
R>Кто что думает?
Можно так: вместо строки хранить в исключении id строки сообщения. Обработчик по id залезет в таблицу строк и покажет сообщение пользователю.
Минусы: на весь проект заводить единую таблицу строк, enum с id сообщений, строки только статические.

Можно так:
class BaseException
{
public:
    virtual void printMessage(std::ostream& out) const;
};

class ExceptionA : public BaseException
{
    int arg1_;
    int arg2_;
public:
    ExceptionA(int arg1, int arg2): arg1_(arg1), arg2_(arg2) {}
public:
    virtual void printMessage(std::ostream& out) const
    {
        out << "My Message " << arg1_ << " " << arg2_;
    }
};

class ExceptionB : public BaseException
{
    int arg_;
public:
    explicit ExceptionB(int arg): arg_(arg) {}
public:
    virtual void printMessage(std::ostream& out) const
    {
        out << "Another Message " << arg_;
    }
};

Минусы: Строки в аргументах конструктора исключения поломают всю красоту с не выделением памяти

Можно еще так:
class Excepton 
{
    const char* sourceName_;
    int line_;

public:
    Exception(const char* sourceName, int line): sourceName_(sourceName), line_(line) {}
    void printMessage(std::ostream& out) const
    {
        out << sourceName_ << ":" << line_;
    }
};

Кидать для удобства через макрос:
#define MY_THROW throw Exception(__FILE__, __LINE__)

Т.к. под макросом __FILE__ компилятор прячет статическую переменную, то выделять память под Excepton::sourceName_ не нужно.

В конструкторе Exception можно немного "подрихтовать" sourceName: оставить только имя файла, полный путь отрезать.
Такое описание исключения получается достаточно информативным. Хотя таким образом сообщать пользователю о не найденном файле не лучший вариант

Еще вариант: все три (два с половиной, последние два сильно похожи один на другой) предложенных выше варианта скрестить

Алексей
Re[4]: throw, wchar_t* и память
От: Seal08  
Дата: 15.04.08 09:35
Оценка:
Здравствуйте, Bell, Вы писали:

__>>Я теперь понял, что происходит при передаче по ссылке: вызывается конструктор копирования

B>Это заблуждение. Дело не в передаче по ссылке, а в том, как именно создается объект — исключения. А создается он как копия объекта, указанного в throw (здесь
Автор: Bell
Дата: 02.12.03
). Так что ссылка в catch совершенно не при чем.


Копия создаётся именно в catch, например если написать

catch(const CSomeException& exc)


а не

catch(CSomeException& exc)


то никакой копии создаваться не будет и программа отработает нормально (только что проверил в gcc).
Re[5]: throw, wchar_t* и память
От: jazzer Россия Skype: enerjazzer
Дата: 15.04.08 09:38
Оценка:
Здравствуйте, Seal08, Вы писали:

S>Копия создаётся именно в catch, например если написать

S>то никакой копии создаваться не будет и программа отработает нормально (только что проверил в gcc).

По стандарту, если я не ошибаюсь, копий объекта исключения может нагенериться сколько угодно независимо от способа ловли.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: throw, wchar_t* и память
От: Bell Россия  
Дата: 15.04.08 09:40
Оценка:
Здравствуйте, Seal08, Вы писали:
S>Копия создаётся именно в catch, например если написать

S>
S>catch(const CSomeException& exc)
S>


S>а не


S>
S>catch(CSomeException& exc)
S>


S>то никакой копии создаваться не будет и программа отработает нормально (только что проверил в gcc).


Повторюсь: здесь
Автор: Bell
Дата: 02.12.03

Если этого недостаточно — смотри 15.1/3.
Любите книгу — источник знаний (с) М.Горький
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.