Обновить

Комментарии 3

Сарказм: через 5 лет ребятки осознают, что в целочисленных числах есть фундаментальная проблема с точностью при операциях деления, и предложат всё перевести в плавающую точку!

---

Это какая-то имитация бурной деятельности. Ограничения типа (по хорошему) должны соответствовать самой сущности (и ограничивать неправильное использование), иначе это превращается в костыли. Когда же пытаются сделать идеализированно - получается много попоболи.

Знаковые числа тоже обладают рядом "косяков":

...-дцать лет назад пытался пофиксить библиотеку Corba на C#: там для номера сетевых портов использовалось знаковое двубайтное число: двубайтное - чтобы значение заталкивать в протокол; знаковое - потому что C#. И ЭТО ПРЕВРАТИЛОСЬ В ЦИРК: эти значения портов постоянно преобразовывались; и в основной функциональности это ещё как-то работало. Как только отходил немного в сторону - то библиотека работала только с сетевыми портами до 327хх. Было весело, результат - решили отказаться от C#.

Ну и классическое: модуль знакового числа (например, двубайтного) иногда не помещается в свои же размеры. Это периодически используется (например, обработка звука: вычисление амплитуды) и если отдельно за этим не следить, то налетаешь "на всякое".

Мой персональный адочек с целочисленными преобразованиями:

off64_t lseek64(int fd, off64_t offset, int whence);
lseek64(fd, -sizeof(envelope)-0x10, SEEK_END);

Работает на 64-битных системах и не работает на 32-битных.
Понимаете, почему?

Я конечно же рукожоп и сам виноват.
Но мое мнение - неявные преобразования между знаковыми и беззнаковыми надо запретить или хотя-бы ограничить. Человек должен явно все прописать и понимать, что он делает.

Кажется, автор еще забыл упомянуть, что переполнение знаковых - это undefined behavior в C++, если делать offset/length знаковыми, то это придется как-то чинить.

Но совсем избавляться от беззнаковых тоже кажется не самой лучшей идеей, тогда, как в Яве, придется вводить оператор “>>>” (беззнаковый сдвиг вправо), и будут проблемы с поддержкой форматов хранения, где значения беззнаковые, например, массив uint8.

Но когда корректность важнее скорости, то имхо, знаковые оффсеты и длины массивов кажутся хорошим компромисом: можно вставить дополнительные проверки на неотрицательность при создании массива и обращении к его элементам (в 99% случаев оно не скажется на производительности, так как проверка будет на свободном ALU-порту, и branch prediction тоже отработает параллельно)

В общем, мне нравится подход как в C#, придуманный много лет назад:

  • длины и оффсеты массивов знаковые, исключения при обращениях out-of-bounds

  • есть беззнаковые, если нужны, более-менее разумные правила автоматического преобразования

  • если важно не поймать overflow, есть опциональный checked{} контекст, в котором оно вызовет исключение

  • Eсли надо выжать последние полпроцента производительности, есть unsafe{} контекст, в котором можно создать массив байт через malloc, и дальше уже с ним извращаться, как душе угодно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации