cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
91 stars 0 forks source link

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

Open romasandu-gaijin opened 1 year ago

romasandu-gaijin commented 1 year ago

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

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 commented 1 year ago

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

void foo(int x);
bool foo_is_valid_input(int x); 
vtopunov commented 1 year ago

gls::narrow GSL

romasandu-gaijin commented 1 year ago

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

romasandu-gaijin commented 1 year ago

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

vtopunov commented 1 year ago

@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 commented 7 months ago

@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.