Здравствуйте, Videoman, Вы писали:
V>Как известно в С++20 подвезли возможность использовать try catch блоки внутри constexpr выражений. Стало удобно, но появилась следующая проблема:
V>Есть функция func1(), которая без проблем работает в constexpr режиме и есть внешняя функция func2(), которая никогда не должна выкидывать исключения, которая вызывает внутри себя первую, типа такого:
V>Так вот, стандартный подход с always_false тут не прокатывает и static_assert срабатывает всегда, а мне нужно что бы он срабатывал только если возникло исключение. Как такое можно организовать?
Ну да, для того, чтобы это сработало, компилятор должен исключить этот static_assert из компиляции. А у него для этого нет поводов. Тут нужно как-то адаптировать дизайн. Например, обернуть в constexpr if:
#include <type_traits>
#include <optional>
int func_runtime()
{
// use intrinsics, threads or other staff throw int();
}
constexpr int func_constexpr() noexcept
{
// no optimizationreturn 0;
}
constexpr int func() noexcept
{
if (std::is_constant_evaluated())
return func_constexpr();
else {
try {
return func_runtime();
} catch (...) {
return 0;
}
}
}
int main()
{
constexpr int value1 = func();
int value2 = func();
return value1 + value2;
}
Иногда в для того, что бы код работал в runtime`е быстро, там приходится использовать низкоуровневые возможности, а constexpr не позволяет такое миксовать. Теперь можно писать код, которые будет работать и в том и в том режиме.
Здравствуйте, Videoman, Вы писали:
V>Как известно в С++20 подвезли возможность использовать try catch блоки внутри constexpr выражений. Стало удобно, но появилась следующая проблема:
V>Есть функция func1(), которая без проблем работает в constexpr режиме и есть внешняя функция func2(), которая никогда не должна выкидывать исключения, которая вызывает внутри себя первую, типа такого:
V>Так вот, стандартный подход с always_false тут не прокатывает и static_assert срабатывает всегда, а мне нужно что бы он срабатывал только если возникло исключение. Как такое можно организовать?
Здравствуйте, Videoman, Вы писали:
V>Делал именно таким методом, но было подумал, что занимаюсь извращением и как-то сложно получается. Вот решил спросить у более опытных коллег. Интересно имменно в рамках 20-го стандарта.
Ну, ждём более опытных
--
Справедливость выше закона. А человечность выше справедливости.
R>Но в таком варианте выражение T::some_func() заведомо не может быть источником исключения, иначе это просто не скомпилируется (или я чего-то не знаю). Отсюда снова вопрос о ценности try-catch в constexpr функции. Вопрос же именно о ценности, а не о том, какова могла быть предыстория образования данного кода.
Так там не зря же вызывается some_func из T. В зависимости от T это могут быть как some_func с исключениями внутри, так и без исключений.
Грубо говоря:
struct constexpr_ready_traits {
static constexpr int some_func() { return 0; }
};
struct runtime_only_traits {
static int some_func() {
if(is_appropriate_moon_phase()) throw std::runtime_error{ "Not now, bro!" };
return 42;
}
};
constexpr int f1 = f<constexpr_ready_traits>();
int f2 = f<runtime_only_traits>();
Вроде все сходится.
Тут другой вопрос: зачем внутри f пытаться делать проверку на наличие исключений в compile-time, но этот вопрос тов.Videoman уже объяснил.
Здравствуйте, Videoman, Вы писали:
S>>А как в рамках C++20 (и даже C++23) бросить исключение в compile-time?
V>Также как с выделением памяти. Если весь процесс остается внутри и все сайд эффекты compile-time, но всё работает. Сама внешняя функция noexcept, наружу ничего не летит.
Простите, понятнее не стало. В C++20 разве можно написать constexpr-функцию, которая бросает исключение в compile-time?
А если нет, то зачем защищаться от исключений, которые в compiler-time могут возникнуть, если возникнуть им неоткуда?
Здравствуйте, Videoman, Вы писали:
V>Здравствуйте, so5team, Вы писали:
V>Ну запутали, так запутали!!!
Простите, не понял, что означает ваш ответ с фейспалмом.
И получается какая-то странная ситуация: вы задаете вопрос, я пытаюсь набросать решение и сталкиваюсь с тем, что не могу его протестировать, т.к. в C++20 (C++23) нет возможности бросить исключение из constexpr-функции в компайл-тайм. Понимаю, что чего-то не понимаю в постановке задачи, задаю уточняющие вопросы, а в ответ фейспалм.
Как известно в С++20 подвезли возможность использовать try catch блоки внутри constexpr выражений. Стало удобно, но появилась следующая проблема:
Есть функция func1(), которая без проблем работает в constexpr режиме и есть внешняя функция func2(), которая никогда не должна выкидывать исключения, которая вызывает внутри себя первую, типа такого:
Так вот, стандартный подход с always_false тут не прокатывает и static_assert срабатывает всегда, а мне нужно что бы он срабатывал только если возникло исключение. Как такое можно организовать?
Здравствуйте, Videoman, Вы писали:
V>Как известно в С++20 подвезли возможность использовать try catch блоки внутри constexpr выражений. Стало удобно, но появилась следующая проблема:
V>Есть функция func1(), которая без проблем работает в constexpr режиме и есть внешняя функция func2(), которая никогда не должна выкидывать исключения, которая вызывает внутри себя первую, типа такого:
V>Так вот, стандартный подход с always_false тут не прокатывает и static_assert срабатывает всегда, а мне нужно что бы он срабатывал только если возникло исключение. Как такое можно организовать?
Интуиция подсказывает, что здесь можно как-то с пользой использовать атрибут assume, но что-то не соображу, как именно.
--
Справедливость выше закона. А человечность выше справедливости.
Делал именно таким методом, но было подумал, что занимаюсь извращением и как-то сложно получается. Вот решил спросить у более опытных коллег. Интересно имменно в рамках 20-го стандарта.
Тут масса вариавнов: можно через pair, можно через option, но в любом случае, приходится городить массу кода для преобразования типов исключений в сообщения об ошибках.
Здравствуйте, so5team, Вы писали:
S>А как в рамках C++20 (и даже C++23) бросить исключение в compile-time?
Также как с выделением памяти. Если весь процесс остается внутри и все сайд эффекты compile-time, но всё работает. Сама внешняя функция noexcept, наружу ничего не летит.
Здравствуйте, so5team, Вы писали:
S>Простите, не понял, что означает ваш ответ с фейспалмом.
Да я не вам вообще, я на ситуацию в общем.
Я только погружаюсь в 20-й стандарт. Просто задал вопрос, а вы мне в ответ тоже вопрос, да еще как-то неуверенно, может/не может кидать исключения
Из-за того, что не понятно сработают на этапе компиляции constexpr выражения или нет, у меня возникла путаница в коде в котором вперемешку constexpr/не constexpr реализация через std::is_constant_evaluated.
Теперь мне понятно как это работает:
в runtime версии возможно throw, а в constexpr — нет, но просто синтаксис try блоков теперь не отпугивает компилятор.
После паузы я и написал — как все запутано в стандарте.
Здравствуйте, Videoman, Вы писали:
V>Я только погружаюсь в 20-й стандарт. Просто задал вопрос, а вы мне в ответ тоже вопрос, да еще как-то неуверенно, может/не может кидать исключения
У меня у самого путаница в стандартах: в каких-то разрешили динамическую память в виде std::string (вроде бы в C++23), в каких-то вроде как и выброс исключений в compile-time разрешили (емнип, в C++26).
Поэтому если смотреть на самые свежие стандарты, то вроде как там можно столкнуться с проблемой, про которую вы спрашиваете. И может быть вы хотите сейчас, уже в рамках C++20 защититься о того, что добавили в С++26. Но проверить гипотетическое решение в рамках C++20 (и вроде бы даже в рамках C++23) не представляется возможным, т.к. в этих стандартах constexpr-функции не бросают исключений.
V>После паузы я и написал — как все запутано в стандарте.
Да не, вроде бы в конкретном стандарте все более-менее. А вот если пытаться прикидывать на перспективу, на тот же C++26, который когда-нибудь компиляторами будет поддержан, то да, становится запутанно.
Здравствуйте, so5team, Вы писали:
S>Поэтому если смотреть на самые свежие стандарты, то вроде как там можно столкнуться с проблемой, про которую вы спрашиваете. И может быть вы хотите сейчас, уже в рамках C++20 защититься о того, что добавили в С++26. Но проверить гипотетическое решение в рамках C++20 (и вроде бы даже в рамках C++23) не представляется возможным, т.к. в этих стандартах constexpr-функции не бросают исключений.
Не, так далеко я не заглядываю. Действуем потихоньку, сначала 20-й, дальше посмотрим.
Для меня главные две фишки С++20 сейчас, это std::is_constant_evaluated() и concept`ы. Вот и возникают некоторые вопросы, возможно уже всем понятные, по мере переползания на новый стандарт.
А ваши контор-вопросы меня просто в тупик поставили и пришлось самому погружаться в тему и разбираться, поэтому и не смог ничего внятного ответить.
Здравствуйте, Videoman, Вы писали:
V>А ваши контор-вопросы меня просто в тупик поставили и пришлось самому погружаться в тему и разбираться, поэтому и не смог ничего внятного ответить.
Здравствуйте, Videoman, Вы писали:
V>Писать в одной функции две ветки кода, одна для вычисления на уровне компиляции, а другая в run-time.
Я вот тоже не очень понимаю, какую пользу можно извлечь из возможности писать try-catch в constexpr функции. Что может бросить исключение из того, что допустимо использовать внутри функции? А если ничего не может, то зачем тогда try-catch?
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
V>>Писать в одной функции две ветки кода, одна для вычисления на уровне компиляции, а другая в run-time.
R>Я вот тоже не очень понимаю, какую пользу можно извлечь из возможности писать try-catch в constexpr функции. Что может бросить исключение из того, что допустимо использовать внутри функции? А если ничего не может, то зачем тогда try-catch?
А возможен ли такой сценарий: у человека была обычная функция, которая содержала внутри себя try-catch, но которую хотелось иметь еще и в виде constexpr, но раньше нельзя было это делать, т.к. там были try-catch.
Грубо говоря, было:
template<typename T>
int f() {
try {
return T::some_func();
}
catch(...) {
// Ну не шмогли, ну и ладно.return 0;
}
}
Теперь такая возможность появляется (т.е. функцию f из примера выше можно объявить constexpr) но не хочется "проглатывать" исключения, если они происходят в compile-time.
S>Теперь такая возможность появляется (т.е. функцию f из примера выше можно объявить constexpr) но не хочется "проглатывать" исключения, если они происходят в compile-time.
Да, только в этом сценарии отсутсвует долгожданное "try-catch в constexpr функции"
--
Справедливость выше закона. А человечность выше справедливости.
S>>Теперь такая возможность появляется (т.е. функцию f из примера выше можно объявить constexpr) но не хочется "проглатывать" исключения, если они происходят в compile-time.
R>Да, только в этом сценарии отсутсвует долгожданное "try-catch в constexpr функции"
Почему отсутствует? Была просто f() показанная выше, стала:
template<typename T>
constexpr int f() {
try {
return T::some_func();
}
catch(...) {
// Ну не шмогли, ну и ладно.return 0;
}
}
Но в таком варианте выражение T::some_func() заведомо не может быть источником исключения, иначе это просто не скомпилируется (или я чего-то не знаю). Отсюда снова вопрос о ценности try-catch в constexpr функции. Вопрос же именно о ценности, а не о том, какова могла быть предыстория образования данного кода.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Videoman, Вы писали:
V>Теперь можно писать код, которые будет работать и в том и в том режиме.
Возможно, я что-то упускаю, но вся эта чехарда с constexpr/consteval больше похожа на поддержку очередного набора трюков, нежели на средства выражения замысла программиста.
Поскольку компилятор всегда видит все зависимости между данными, ему не требуется явного указания constexpr, чтобы вычислить при компиляции выражение, если оно технически вычислимо. Единственное, что здесь может быть полезно — это consteval/constinit, чтобы получить ошибку, если выражение не удается вычислить во время компиляции.
Соответственно, функция вроде Вашей сильно смахивает на дверь, в петлю которой вставлена дужка незапертого замка. То ли пытались запереть, но забыли или не сумели, то ли просто не нашлось ничего более подходящего, чтоб сама не распахивалась.
Здравствуйте, Евгений Музыченко, Вы писали:
V>>Теперь можно писать код, которые будет работать и в том и в том режиме.
ЕМ>Возможно, я что-то упускаю, но вся эта чехарда с constexpr/consteval больше похожа на поддержку очередного набора трюков, нежели на средства выражения замысла программиста.
И?
Даже если вы и правы, то что следует? Следует перестать использовать constexpr/consteval? Следует написать пропозал об отмене constexpr/consteval и попробовать провести его через комитет, чтобы в C++29 и C++32 ключевые слова constexpr/consteval для функций/методов сделали опциональными?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Соответственно, функция вроде Вашей сильно смахивает на дверь, в петлю которой вставлена дужка незапертого замка. То ли пытались запереть, но забыли или не сумели, то ли просто не нашлось ничего более подходящего, чтоб сама не распахивалась.
Проблема не в том, что бы вычислять на этапе компиляции, а в том, что не хочется писать код отдельно для compile-time и отдельно для run-time. Представь, что 95% кода работает именно так, как ты говоришь, но где-то в потрохах вызывается функция, которая не может быть выполнена в compile-time. Теперь такое можно поддерживать, просто имея две реализации одной функции, имея остальной код в единственном экземпляре.