Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[35.173.48.18] |
|
Сообщ.
#1
,
|
|
|
Здравствуйте!
С удивлением обнаружил, что noexcept влияет на тип шаблона. Имеется класс: template< typename CallableType > class callable_wrapper; template< typename ResultType, typename ...ArgsType > class callable_wrapper< ResultType( * )( ArgsType... ) > { public: using result_type = ResultType; }; Шаблон класса callable_wrapper, для обёртки указателя на функцию и вызова этой функции. (Здесь я порезал код вызова.( Он у меня используется в качестве базового класса, если кому-то интересно. Объект создаётся явно, нечто вроде: callable_wrapper< decltype( &funcname ) >( &funcname ); Внезапно, выяснил, что этот шаблон класса, принимает указатели на функции без noexcept. А если передать noexcept, то оно не компилируется. Поправил шаблон: template< typename ResultType, typename ...ArgsType > class callable_wrapper< ResultType( * )( ArgsType... ) noexcept > { public: using result_type = ResultType; }; И получилось наоборот. Принимает только noexcept, а без noexcept уже ошибка. Попытался вывести параметр: template< typename ResultType, typename ...ArgsType, bool IsNoexcept > class callable_wrapper< ResultType( * )( ArgsType... ) noexcept( IsNoexcept ) > Но в таком варианте не компилируется, невозможно вывести параметр шаблона. Возникло несколько вопросов: |
Сообщ.
#2
,
|
|
|
Интересный вопрос! Но ответа, увы, нет. Подписываюсь на уведомления
Добавлено Цитата Eric-S @ принимает указатели на функции без noexcept Как вариант для попытки решения ... может как-то впилить в код обертку в виде std::function? Может это как-то поможет... |
Сообщ.
#3
,
|
|
|
[QUOTE=Majestio,1654072481,3867072]принимает указатели на функции без noexcept[/quote]
Как вариант для попытки решения ... может как-то впилить в код обертку в виде std::function? Может это как-то поможет...[/QUOTE] Хотелось бы получить вариант лёгкого кода, без излишеств, самый минимум, только хардкор. Увы, std::function его заметно утяжеляет. Пока решил сделать две специализации, для noexcept( false ) и noexcept( true ), добавив шаблону параметр bool IsNoexcept. А этот самый параметр устанавливать явно, вызвав noexcept( funcname( args... ) ). Решение грубое, но в теории должно сработать. |
Сообщ.
#4
,
|
|
|
Цитата Eric-S @ Пока решил сделать две специализации, для noexcept( false ) и noexcept( true ), добавив шаблону параметр bool IsNoexcept. Ну да, так конечно легче будет. Согласен. |
Сообщ.
#5
,
|
|
|
Поисследовал проблему. Начиная с C++17 спецификация noexcept является частью типа, поэтому функции (и методы с операторами, естественно), отличающиеся лишь этим спецификатором, частично совместимы друг с другом. Т.е., например, указателю на noexcept(true) можно присвоить noexcept(false), но не наоборот. Однако при сопоставлении типов совместимость не применяется и требуется точное соответствие. Формально реализации способны вывести значение noexcept(), т.к. для каждого типа это представляет собой значение периода компиляции, однако Стандарт не содержит требований для этого. В 18-м году было внесено предложение считать это дефектом и открыто голосование. Буквально 7 мая сего года оно было одобрено.
|
Сообщ.
#6
,
|
|
|
Цитата Qraizer @ однако Стандарт не содержит требований для этого. В 18-м году было внесено предложение считать это дефектом и открыто голосование. Буквально 7 мая сего года оно было одобрено. О как! Спасибо! Видимо я как раз наткнулся. Компилятор ведёт себя странно. Параметр не выводит. Путается в типах. |
Сообщ.
#7
,
|
|
|
Шаманил. И так и сяк крутил. Не хотел компилятор различать noexcept в шаблонах.
В итоге сляпал грубый костыль для получения типа указателя на функцию, без модификатора noexcept. Шаблон remove_noexcept, первым параметром тип указателя на функцию, а вторым параметром флаг есть ли noexcept. Увы, у меня автоматически не определяет наличие noexcept, только ругается. template< typename CallableType, bool IsNoexcept > struct remove_noexcept; template< typename ResultType, typename ...ArgsType > struct remove_noexcept< ResultType( * )( ArgsType... ), false > { using type = typename ResultType( * )( ArgsType... ); }; template< typename ResultType, typename ...ArgsType > struct remove_noexcept< ResultType( * )( ArgsType... ) noexcept, true > { using type = typename ResultType( * )( ArgsType... ); }; template< typename ResultType, typename ...ArgsType > struct remove_noexcept< ResultType( & )( ArgsType... ), false > { using type = typename ResultType( * )( ArgsType... ); }; template< typename ResultType, typename ...ArgsType > struct remove_noexcept< ResultType( & )( ArgsType... ) noexcept, true > { using type = typename ResultType( * )( ArgsType... ); }; template< typename ResultType, typename ...ArgsType > struct remove_noexcept< ResultType( *& )( ArgsType... ), false > { using type = typename ResultType( * )( ArgsType... ); }; template< typename ResultType, typename ...ArgsType > struct remove_noexcept< ResultType( *& )( ArgsType... ) noexcept, true > { using type = typename ResultType( * )( ArgsType... ); }; Заодно приводит ссылку на функцию к указателю на функцию. Идиологически неверно, надо бы разделить. Ещё можно сделать макрос, чтоб оно хотя бы выглядело пристойно: #define REMOVE_NOEXCEPT( FUNCNAME, ... ) remove_noexcept< decltype( FUNCNAME ), noexcept( FUNCNAME( __VA_ARGS__ ) ) >::type Первый параметр указатель на функцию. Последующие параметры, это параметры передаваемые в функцию. А возвращает тип указателя на функцию. Ну или как-то так. Мой код теперь компилируется. А про рефакторинг подумаю завтра. |
Сообщ.
#8
,
|
|
|
А если проще?
typedef char small_type; typedef struct {char dummy[2];} big_type; template <typename ResultType, typename ...ArgsType> small_type check_noexcept(ResultType (*)(ArgsType...)); template <typename ResultType, typename ...ArgsType> big_type check_noexcept(ResultType (*)(ArgsType...) noexcept); /* проверяем */ void may_throw(); void no_throw() noexcept; constexpr bool throw_true = sizeof(check_noexcept(may_throw)) == sizeof(small_type); constexpr bool throw_false= sizeof(check_noexcept(no_throw)) == sizeof(small_type); Добавлено Эх, были времена, когда в Стандарте не было никакого метапрограммирования. Всё руками, всё руками... |
Сообщ.
#9
,
|
|
|
Цитата Qraizer @ А если проще? Очень интересное и своеобразное решение. Наверное, его можно даже упростить, используя inline constexpr функции, возвращающие bool. Спасибо. Подумаю и попробую. Добавлено Цитата Qraizer @ Эх, были времена, когда в Стандарте не было никакого метапрограммирования. Всё руками, всё руками... Так лениво же руками повторять одно и тоже. А вот что-нибудь этакое, да ещё само выполняющее работу... Так что автоматизация и ещё раз автоматизация. |
Сообщ.
#10
,
|
|
|
Цитата Eric-S @ Во времена C++03 принцип SFINAE руками эксплуатировался настолько часто, что даже сейчас у меня руки этот код написали, почитай, сами, голова в этом не участвовала. Нынче этот SFINAE является основной движущей силой в <type_traits> та и не только там. Ну коли гора к Магомеду задом... мы не гордые, повернём Магомеда передом к горе сами. Конечно лучше это инкапсулировать куда-нибудь. Очень интересное и своеобразное решение. Наверное, его можно даже упростить, используя inline constexpr функции, возвращающие bool. Спасибо. Подумаю и попробую. Добавлено P.S. Формально этот метакод исполняет алгоритм «Если параметр одной из перегрузок check_noexcept() не подходит, выбери другую перегрузку и скажи, какую выбрал; если не выбрал ничего, остановись с ошибкой». Обе перегрузки имеют параметрами указатели на функции, поэтому любые, что не такие, отклонят обоих кандидатов. Те, что подходят, взаимоисключаются спецификатором, поэтому не могут быть выбраны одновременно, так что у компилятора останется ровно один кандидат. Какой именно, определяется возвращаемым значением, которые спецом выбраны так, что имеют гарантированно разные размеры. Плюс эксплуатируется тот факт, что sizeof() всегда исполняется на стадии компиляции. |
Сообщ.
#11
,
|
|
|
Цитата Qraizer @ Нынче этот SFINAE является основной движущей силой в <type_traits> та и не только там. Ну коли гора к Магомеду задом... мы не гордые, повернём Магомеда передом к горе сами. Конечно лучше это инкапсулировать куда-нибудь. Формально этот метакод исполняет алгоритм «Если параметр одной из перегрузок check_noexcept() не подходит, выбери другую перегрузку и скажи, какую выбрал; Механизм понятен. Но по-моему в нём участвуют лишние сущности. Взял идею, переработал и получил следующий рабочий код: /// @brief получает тип указателя на функцию /// @param CALLABLE вызываемая функция. /// @return тип указателя на функцию. #define CALLABLE_TYPE( CALLABLE ) \ callable_type< decltype( CALLABLE ), decltype( has_noexcept( CALLABLE ) ) >::type /// @brief проверка установленного модификатора noexcept /// @param[in] callable проверяемый указатель на функцию /// @return результат проверки. template< typename ResultType, typename ...ArgsType > [[nodiscard]] inline constexpr auto has_noexcept( [[maybe_unused]] ResultType( *callable )( ArgsType... ) ) noexcept -> false_type { return false_type(); } /// @brief проверка установленного модификатора noexcept /// @param[in] callable проверяемый указатель на функцию /// @return результат проверки. template< typename ResultType, typename ...ArgsType > [[nodiscard]] inline constexpr auto has_noexcept( [[maybe_unused]] ResultType( *callable )( ArgsType... ) noexcept ) noexcept -> true_type { return true_type(); } /// @brief преобразует вызываемый тип в тип указателя на функцию /// @tparam CallableType тип вызываемой функции. /// @tparam IsNoexcept флаг установки noexcept. template< typename CallableType, typename IsNoexcept > struct callable_type; /// @brief преобразует вызываемый тип в тип указателя на функцию /// @tparam ResultType тип возвращаемого результата. /// @tparam ArgsType тип аргументов вызова. template< typename ResultType, typename ...ArgsType > struct callable_type< ResultType( * )( ArgsType... ), false_type > { using type = typename ResultType( * )( ArgsType... ); }; /// @brief преобразует вызываемый тип в тип указателя на функцию /// @tparam ResultType тип возвращаемого результата. /// @tparam ArgsType тип аргументов вызова. template< typename ResultType, typename ...ArgsType > struct callable_type< ResultType( * )( ArgsType... ) noexcept, true_type > { using type = typename ResultType( * )( ArgsType... ); }; |