Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.224.147.211] |
|
Страницы: (6) [1] 2 3 ... 5 6 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Здравствуйте!
Есть шаблонный класс. В него можно передать любой тип typename ValueType. Мне хотелось бы знать, есть ли у этого ValueType для конструктора и др членов, модификатор noexcept. Ну... Э... Если очень упростить, то как-то так: template< typename ValueType > class someclass { public: /* тут я руками вызываю конструктор в моём размещении. */ void construct() noexcept( noexcept_constructor< ValueType >::value ) { new( &_value ) ValueType(); } private: /* тут, размещается сам объект. */ char _value[ sizeof( ValueType ) ]; }; То есть, я хочу скопировать модификатор noexcept для своей функции "construct" Если он есть у конструктора ValueType то у меня noexcept( true ), иначе noexcept( false ). Это же косается конструкторов копирования, перемещения, операторов копирования, перемещения и деструктора. Хорошо бы и для остальных конструкторов и операторов присвоения это получить, да и для произвольных функций тоже. Про хидер "type_traits" знаю. Очень внимательно, несколько раз перечитал описание его сущьностей... А вот какой взять, так и не могу выбрать. Чего-то их слишком много! |
Сообщ.
#2
,
|
|
|
is_nothrow_constructible не то?
|
Сообщ.
#4
,
|
|
|
Ага. Точно. Читал давно, но неиспользовал и забыл где была фича. Вроде, как юзать выражение, я разобрался. noexcept( ValueType() ) Возвращает, инфу о конструкторе, true если он noexcept, иначе false. А вот как узнать деструктор, чего-то не понимаю. noexcept( ~ValueType() ) Возвращает false, хотя у меня для проверяемого объекта точно true. |
Сообщ.
#5
,
|
|
|
Батенька, деструкторы всегда nothrow.
|
Сообщ.
#6
,
|
|
|
Цитата Flex Ferrum @ Батенька, деструкторы всегда nothrow. :) Ннет, не всегда. Есть оговорки. Например одно из предложений для std::expected, чтоб деструктор бросал исключение, если разработчик забыл проверить код ошибки. Но не надо холиваров, ладно? Но я разобрался, как проверить деструктор. Проблемы были из-за нескольких наложившихся глюков. Во-первых, я накосячил с перекомпиляцией объектных файлов. Поленился в makefile указать зависимость. Ну и прога выдала мне устаревшую инфу. Во-вторых, visual studio, оказывается скрыто добавляет для некоторых деструкторов спецификацию noexcept(true). Это можно отключить явно указав noexcept( false ) или ключиком в командной строке. Ну и да, noexcept( ~ValueType() ) вернул мне правильную инфу о деструкторе. Вот как в деструкторе проставил true или false, то и вернул. |
Сообщ.
#7
,
|
|
|
Цитата Eric-S @ Ннет, не всегда. Есть оговорки. Например одно из предложений для std::expected, чтоб деструктор бросал исключение, если разработчик забыл проверить код ошибки. Но не надо холиваров, ладно? Вот без холиваров. А что вообще может означать исключение в деструкторе? Объект нельзя удалить? |
Сообщ.
#8
,
|
|
|
Цитата Eric-S @ char _value[ sizeof( ValueType ) ]; К слову, тут тебе ещё надо выравнивание специфицировать корректное. |
Сообщ.
#9
,
|
|
|
Цитата Олег М @ А что вообще может означать исключение в деструкторе? Объект нельзя удалить? Ну, бонально вернуть информацию о возникших проблемах. Для примера, чисто гипотетического, возьмём некую обёртку над хэндлом файла в системе windows. Конструктор файл открывает функцией CreateFile, а деструктор файл закрывает функцией CloseHandle. А функция CloseHandle возвращает код ошибки. Вы меня как-то упрекали за коды возвратов, хотя я как раз предпочитаю бросать исключения, чтоб знать обо всех возникших проблемах. Ну и CloseHandle может вернуть ошибку, которую бросаем в исключении. Причём, именно из деструктора. Это плохо. Это бяка. Но всяко лучше упасть и знать причину падения, чем упасть через std::terminate() и знать, лишь, что где-то случилась проблема. Добавлено Цитата Flex Ferrum @ Цитата Eric-S @ char _value[ sizeof( ValueType ) ]; К слову, тут тебе ещё надо выравнивание специфицировать корректное. У меня сейчас сделано поле в union{ value_type _value }; Но про выравнивание, замечание справедливое. Смотрю в сторону std::aligned_storage, но пока не выяснил точно, как его правильно юзать. А у меня, по сабжу, ещё вылез косяк. template< typename ...Args > void construct( Args ... args ) noexcept( value_type( args... ) ) { new( ADDRESS_OF( this->_value ) ) value_type( args... ); } Компилятор ругается, что я передал неконстантное выражение. Добавлено Цитата Олег М @ is_nothrow_constructible не то? Упс. Я разобрался. Действительно, для конструкторов - самое то. Всё получилось очень просто. template< typename ...Args > void construct( Args ... args ) noexcept( std::is_constructible< value_type, Args... >::value ) { new( ADDRESS_OF( this->_value ) ) value_type( args... ); } |
Сообщ.
#10
,
|
|
|
Цитата Eric-S @ noexcept( std::is_constructible Здесь все правильно, именно is_constructible? |
Сообщ.
#11
,
|
|
|
Цитата Eric-S @ Ну и CloseHandle может вернуть ошибку, которую бросаем в исключении. Причём, именно из деструктора. Это плохо. Это бяка. Но всяко лучше упасть и знать причину падения, чем упасть через std::terminate() и знать, лишь, что где-то случилась проблема. На мой взгляд, даже если эта ошибка так крайне необходима, то лучше сохранить её здесь же куда-нибудь в лог и продолжить выполнение деструктора. Иначе получится объект, удалённый наполовину. И что с этим потом делать? |
Сообщ.
#12
,
|
|
|
Цитата Олег М @ Цитата Eric-S @ noexcept( std::is_constructible Здесь все правильно, именно is_constructible? Ой, нет... Надо код проверить, неужели я там так же накосячил?... Добавлено И в коде та же бяка. К счастью только в одном месте, в остальных как и положено nothrow. Что-то я вчера поторопился и накосячил. Но хорошо, что заметили. Добавлено Цитата Олег М @ На мой взгляд, даже если эта ошибка так крайне необходима, то лучше сохранить её здесь же куда-нибудь в лог и продолжить выполнение деструктора. Иначе получится объект, удалённый наполовину. И что с этим потом делать? Если ошибка в деструкторе, то именно, что сохранять в лог, а затем убивать программу. Ну и да... Запарно лезть в каждый деструктор, чтобы там менять код записи в лог. Я раньше юзал макросы _RPTF и типа того, для записи, но это не совсем то, что хотелось бы. Сейчас, у меня есть класс, где можно зарегистрировать обработчик исключения. Ну, а где-нибудь в деструкторе пишу: ~someclass() noexcept( true ) { try { ... close_work(); ... } catch( ... ) { on_exception( std::current_exception() ); } } Но это уже моя практика. А так, если не перехватывать, то деструктор, очень даже, может стрельнуть исключением. При этом сработает noexcept, которая вызовет std::terminate() и что вообще сломалось останется долго гадать. При этом раскрутка стека вообще не выполнится. |
Сообщ.
#13
,
|
|
|
Цитата Eric-S @ И в коде та же бяка. К счастью только в одном месте, в остальных как и положено nothrow. Что-то я вчера поторопился и накосячил. Но хорошо, что заметили. Да уж, ошибка хорошая. Не знаю, как в виндах, а в линуксе noexcept выкидывает, при исключении, чёрт знает куда, концов не найдёшь. Добавлено Цитата Eric-S @ Сейчас, у меня есть класс, где можно зарегистрировать обработчик исключения. Ну, а где-нибудь в деструкторе пишу: Ну, у меня примерно также. Только я заворачиваю в отдельный try-catch (через макрос) каждую функцию, которая вызывается в деструкторе и может вызвать исключение. Чтобы гарантировать, что они все выполнятся, ну или попытаются выполниться. |
Сообщ.
#14
,
|
|
|
Цитата Олег М @ Да уж, ошибка хорошая. Не знаю, как в виндах, а в линуксе noexcept выкидывает, при исключении, чёрт знает куда, концов не найдёшь. Под виндой, нормально, с моей точки зрения. Сам компилятор visual studio, поддерживает стандартные исключения, через внутренний механизм структурных исключений. Ну а когда прога стреляет исключением, то более менее понятно, откуда оно прилетело. Есть свои заморочки. Под виндой нет удобной трассировки стека в C++. Реализовано через хитрые отладочные функции. Поэтому надо писать свои обёртки и подключать их чудным способом. Зато, в результате, получается очень даже информативно. Так или иначе, даже без отладчика, можно понять, откудо взялось то или иное исключение. Хотя по началу, надо разобраться с механизмом, который не слишком очевиден. Добавлено Цитата Олег М @ Ну, у меня примерно также. Только я заворачиваю в отдельный try-catch (через макрос) каждую функцию, которая вызывается в деструкторе и может вызвать исключение. Чтобы гарантировать, что они все выполнятся, ну или попытаются выполниться. Вообще-то и у меня макросы. Для форума их раскрыл, чтоб было нагляднее. У себя пишу ~someclass() noexcept( true ) { START_EXCEPTION_MONITOR ... STOP_EXCEPTION_MONITOR } По количеству букв даже длинее, но по числу занимаемых строк короче. Ну и в случае чего, содержимое макросов легче переделать. А таковое изменение, распространится на весь проект. Уже давно собираюсь сделать отдельные обработчики для каждой нити. Сейчас один обработчик на весь процесс. Но раздумываю, надо оно или нет. Пока делать лень. Добавлено Цитата Олег М @ каждую функцию, которая вызывается в деструкторе и может вызвать исключение. Чтобы гарантировать, что они все выполнятся, ну или попытаются выполниться. А вот оборачивать каждую вызываемую функцию... Это... Ну... Изврат уже какой-то. Не, нафиг. Так может чего-нибудь наложится и побочные эффекты замучат. Лучше уж сразу весь деструктор прерывать, на первой же ошибке. А вообще, я стараюсь в деструкторе вызывать только одну функцию. И вообще не писать там сложного кода. Сложный код, должен находится в методах, которые вызывает пользователь. А деструктор, всего лишь подчищает ресурсы. Выше приводил гипотетический пример для обёртки хэндла файла. В деструкторе вызывается только функция CloseHandle. А если уж совсем правильно, то надо примерно так: void close() { if( INVALID_HANDLE_VALUE != this->_handle_file ) { if( FALSE == ::CloseHandle( this->_handle_file ) ) { auto error_code = ::GetLastError(); throw std::system_error( error_code, std::system_category(), "CloseHandle in file::close()" ); } this->_handle_file = INVALID_HANDLE_VALUE; } } А пользователь класса, явно вызывает метод close() для закрытия файла. В таком случае, деструктор лишь проверяет, что хэндл закрыт и завершается. Если пользователь забыл или не смог закрыть файл, то тогда деструктор автоматически закрывает открытый файл. Эм-эм-эм... Вот вообще, ни разу, не помню, чтоб CloseHandle() возвращала ошибку, если ей передавали правильный хэндл. Но, вероятность такая есть, так что пусть будет исключение, на тот самый, невероятный случай. Хотя... Вот есть иная, тоже интересная задачка. Это классы scope_exit, scope_success, scope_failure. Они в конструкторе принимают лямду. А в деструкторе эту лямду вызывают. И эти лямды, могут ведь тоже бросить исключения! |
Сообщ.
#15
,
|
|
|
Цитата Eric-S @ По количеству букв даже длинее, но по числу занимаемых строк короче. Ну и в случае чего, содержимое макросов легче переделать Ну, у меня основная идея в том, что надо каждый вызов заворчивать в подобную конструкцию. Если у тебя не отработала одна функция в деструкторе, почему не должны отработать другие? Добавлено У меня этот макрос выглядит примерно так TS_NOEXCEPT(m_cnn.RollbackTrans()); Добавлено Цитата Eric-S @ Хотя... Вот есть иная, тоже интересная задачка. Это классы scope_exit, scope_success, scope_failure. Они в конструкторе принимают лямду. А в деструкторе эту лямду вызывают. И эти лямды, могут ведь тоже бросить исключения! Я такую штуку довольно активно использую Добавлено Что–то, похоже, одновременно пишем |