Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Безопасные" приведения целочисленных типов #575

Open
romasandu-gaijin opened this issue Aug 27, 2023 · 7 comments
Labels
good first issue Несложная на вид идея. Самое то, чтобы попробовать написать на её proposal и помочь РГ21

Comments

@romasandu-gaijin
Copy link

На горизонте контракты. В связи с этим хочется автоматизировать довольно часто встречающийся на практике отрывок кода:

std::vector<uint16_t> data;
void foo(int x)
{
    assert(0 <= x && x <= std::numeric_limits<uint16_t>::max());
    data.push_back(static_cast<uint16_t>(x));
}

Обычно такое выходит, когда "по дефолту" в публичных интерфейсах ставят простые типы, а потом оказывается, что на самом деле хватит 16 или даже 8 бит, имплементацию меняют для экономии памяти/перфа, а публичное АПИ менять нельзя (не хочется, сложно, ...). Но кажется и другие кейсы не сложно придумать.

Идея следующая:

// <numeric>
namespace std
{
template<std::integral T, std::integral U>
T integral_cast(U u)
    [[pre: std::numeric_limits<T>::min() <= u]]
    [[pre: u <= std::numeric_limits<T>::max()]]
    [[post r: static_cast<U>(r) == u]] // возможно не нужно?
{
    return static_cast<T>(u);
}
}

Таким образом получаем явную передачу намерения "я точно знаю, что должно влезть, приведение по модулю 2^n -- ошибка", плюс автоматическую ловлю багов в режиме проверки контрактов в рантайме.

@kov-serg
Copy link

А почему вы считаете что эта проверка должна быть внутри, а не снаружи?

void foo(int x);
bool foo_is_valid_input(int x); 

@vtopunov
Copy link

gls::narrow GSL

@romasandu-gaijin
Copy link
Author

@kov-serg я не считаю, я беру это за предпосылку. Такой код уже есть, и с ним как-то нужно жить и ловить баги.

@romasandu-gaijin
Copy link
Author

@vtopunov это близко, но в геймдеве исключения запрещены по религиозным причинам, поэтому хочется всё таки аналог ассёрта, то есть контракт.

@vtopunov
Copy link

vtopunov commented Oct 31, 2023

@vtopunov это близко, но в геймдеве исключения запрещены по религиозным причинам, поэтому хочется всё таки аналог ассёрта, то есть контракт.

template<class T, class U>
constexpr T narrow(U value) noexcept
{
assert(std::in_range<T>(value));
return static_cast<T>(value);
}
`

@romasandu-gaijin
Copy link
Author

@vtopunov это близко, но в геймдеве исключения запрещены по религиозным причинам, поэтому хочется всё таки аналог ассёрта, то есть контракт.

template<class T, class U> constexpr T narrow(U value) noexcept { assert(std::in_range<T>(value)); return static_cast<T>(value); } `

Ну да, но операция эта настолько частая, что хочется иметь в стандарте кошерный способ её проводить. Особенно всякие касты между signed и unsigned.

@apolukhin
Copy link
Member

Сами пользуемся подобной функцией, называем её numeric_cast https://userver.tech/d6/d84/namespaceutils.html#a618ea4bf009deb91e754e551b25d9811

Даже было нужное предложение https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1879.htm но что-то оно затихло. Если решитесь заняться идеей, то сначала надо связаться с автором и узнать, что пошло не так

@apolukhin apolukhin added the good first issue Несложная на вид идея. Самое то, чтобы попробовать написать на её proposal и помочь РГ21 label Dec 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Несложная на вид идея. Самое то, чтобы попробовать написать на её proposal и помочь РГ21
Projects
None yet
Development

No branches or pull requests

4 participants