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 · 6 comments

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants