Здравствуйте, sergii.p, Вы писали:
S>>Далеко не глупые люди посмотрели к тему приводит C-шный каст, внедрили в C++ специальные конструкции, которые если не делают код безопаснее, то уж точно явно отмечают подозрительные места...
SP>тут вопрос как раз о конструкциях, которые приводят к простреленным конечностям с C и C++ кастом. Я вот как-то придумать такие конструкции с ходу не могу. SP>Просто "пометить подозрительные места" — мне кажется слишком слабый аргумент для ввода 4 новых слов в язык.
У меня нет под рукой собственной ошибки (хотя компилятор несколько раз в год бьет по рукам в попытках неправильно использовать static_cast, особенно в обобщенном коде). Но вот хороший пример из Интернета:
#include <iostream>
using namespace std;
class Base
{};
class Derived: private Base
{
// Inherited private/protected
// not public
};
// Driver codeint main()
{
Derived d1;
// C-style cast allowed
Base* b1 = (Base*)(&d1);
// static_cast not allowed
Base* b2 = static_cast<Base*>(&d1);
return 0;
}
Здравствуйте, Kernan, Вы писали:
S>>За использование сишного каста в C++ном коде нужно отрывать руки, а затем выгонять на мороз (ну или в обратной последовательности). K>Евангелизм как он есть.
Или слишком много набитых в C++ шишек, которых можно было бы избежать, если бы программисты думали о том, как написать нормальный код, а не о том, чтобы сэкономить несколько символов.
Далеко не глупые люди посмотрели к тему приводит C-шный каст, внедрили в C++ специальные конструкции, которые если не делают код безопаснее, то уж точно явно отмечают подозрительные места... Но нет, всегда есть кто-то шибко умный, который скажет, что у него-то уж точно все нормально.
правила их работы и сценарии их использования описаны вдоль и поперек. Когда в проект подключается новый разработчик, особенно если это не сильно траченный молью опытный молодой программист, объяснить ему правила использования этих конструкций можно тупо сославшись на хорошее руководство по C++.
Но кому-то этого мало, и кто-то допускает в дополнение к этим средствам еще и Си-каст. Причем в стиле: вот тут можно, а вот тут низяя!
Тем самым усложняя набор правил. Как для себя, так и для тех, кто затем будет работать с вами (или над вашим кодом).
Здравствуйте, Marty, Вы писали:
M>Почему сишным кастом пользоваться плохо?
Потому что он умеет втихаря снимать константность и реинтерпретировать. Это позволяет на ровном месте устроить ад и израиль.
Тогда как плюсовые касты явно выражают намерение.
M>Например, я убираю возможную знаковость у char переменной и конвертирую её в unsigned как-то так: M>
M>auto u = (unsigned)(std::uint8_t)ch;
M>
M>Но можно и так: M>
M>auto u = static_cast<unsigned>(static_cast<std::uint8_t>(ch));
M>
Но можно и так
constexpr unsigned unsigned_from_char(char c) { return static_cast<unsigned>(static_cast<unsigned char>(c)); }
constexpr unsigned unsigned_from_char(auto) = delete; // во избежание неявных кастов
...
auto u = unsigned_from_char(ch);
...
M>Во втором случае выражение гораздо менее наглядно, приходится скрипеть мозгами, чтобы понять, что там происходит.
А в третьем случае вызов наглядный, а вся машинерия спрятана под капот, — не приходится скрипеть мозгами, зачем обязательно надо делать два каста, а не сразу единственный (unsigned)ch.
M>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
M>ОбсудиПохоливарим?
Придёт ПМ, включит линтер и-или
-Werror=old-style-cast
(или как оно там в комстроке...) — и холиварь с начальством сколько влезет
Здравствуйте, Marty, Вы писали:
M>Почему сишным кастом пользоваться плохо?
Ну потому что, как правило, сишный каст выполняет преобразование большей силы, чем это реально нужно. Соответственно, он идет в разрез с принципами типовой безопасности C++. Вообще, тема очень избитая, не хотелось бы начинать еще один холивар на эту тему.
M>Например, я убираю возможную знаковость у char переменной и конвертирую её в unsigned как-то так: M>
M>auto u = (unsigned)(std::uint8_t)ch;
M>
M>Но можно и так: M>
M>auto u = static_cast<unsigned>(static_cast<std::uint8_t>(ch));
M>
M>Во втором случае выражение гораздо менее наглядно, приходится скрипеть мозгами, чтобы понять, что там происходит.
Для простых типов такое преобразование эквивалентно C-style cast, но, на мой взгляд, обладает несколькими преимуществами:
1) его невозможно ПО ОШИБКЕ применить к ссылке, указателю или какому-то еще типу, состоящему из двух и более лексем
2) для объектов классов такая запись будет означать инициализацию через конструктор
3) как по мне, у этой записи более приятный вид как по сравнению с C-cast, так и по сравнению с C++-cast записями
Впрочем, все сказанное — это мое имхо, на истину в последней инстанции не претендую.
А то, что исходные данные не просто так константы. Но компилятор не поможет вам защититься от того, что вы их модифицируете. А мог бы.
Искать подобную ошибку (например, когда в многопоточном коде один общий буфер с константными данными разделили между несколькими тредами, а один из них начал эти данные менять по ходу дела) можно будет долго и больно.
S>>Но даже на reinterpret_cast<unsigned char *>(in) компилятор будет ругаться.
M>А на const_cast — не будет. И?
const_cast вам придется написать явно. И тут есть два сценария, когда вы написали reinterpret_cast без const_cast и получили по рукам от компилятора:
1. Вы не обратили внимания на то, что исходный указатель константный (а может и не знали об этом, если все происходит в обобщенном коде, где какой-нибудь Src_Type может быть как const-указателем, так и не-const). Компилятор указал вам на это и вы поняли, что делаете что-то не то. И меняете либо входящий тип (чтобы он не было константным), либо меняете свой код, чтобы обходиться константным указателем. Либо пишете const_cast и молитесь на отсутствие UB.
2. Вы дописали const_cast не приходя в сознание. Ну хочет компилятор, ну на тебе. В этом случае хотя бы это место будет проще найти, т.к. const_cast-ы быстрее бросаются в глаза да и ищутся проще, чем Си-касты.
Т.е. даже в худшем случае вы получаете больше бенефитов, чем от Си-каста.
M>Очередная демагогия
Здравствуйте, Alekzander, Вы писали:
A>Я же написал, что иногда приходится иметь дело с историческими артефактами, но там и Си-каста хватает.
Си-каст? Прострелить ноги, оторвать руки и на мороз шагом марш!
Но вообще, конечно, забавно. Сперва рассуждения о "Любой практически полезный сценарий надо прописывать в явном виде, с if'ами, а не неявно через типопреобразование", а потом бах, и Си-каст, который на все типопреобразования кладет с прибором. Вот что в головах у людей?
Здравствуйте, Maniacal, Вы писали:
M>>Например, я убираю возможную знаковость у char переменной и конвертирую её в unsigned как-то так: M>>
M>>auto u = (unsigned)(std::uint8_t)ch;
M>>
M>Achtung! unsigned это unsigned int, а не unsigned char. Тут один байт в четыре превращается.
Спасибо, кэп, я в курсе. Поэтому я сначала кастую к unsigned типу той же размерности, и только потом к unsigned int'у
M>static_cast, dynamic_cast, reinterpret_cast это в первую очередь инструменты для преобразования указателей базовых классов в унаследованные с контролем или в случае reinterpret_cast явное указание, что не в наследуемый. M>Хотя в рекомендациях пишут, что static_cast можно и нужно использовать даже для встроенных типов.
И я вот как раз про это. Плюсовые касты — это сигнал, что происходит какое-то нетривиальное преобразование. Что бывает достаточно редко. В остальных случаях достаточно сишного каста
Здравствуйте, Marty, Вы писали:
M>У меня очень, очень много рабочего кода, написанного с сишными кастами. В том числе коммерческий (был, сейчас скис, спрос упал) проект, и плагин к Far'у. И это всё как-то работает
Здравствуйте, so5team, Вы писали:
M>>У меня очень, очень много рабочего кода, написанного с сишными кастами. В том числе коммерческий (был, сейчас скис, спрос упал) проект, и плагин к Far'у. И это всё как-то работает
S>Ошибка выжившего.
#include <iostream>
using namespace std;
class Base
{};
class Derived: private Base
{
// Inherited private/protected
// not public
};
// Driver codeint main()
{
Derived d1;
// C-style cast allowed
Base* b1 = (Base*)(&d1);
// static_cast not allowed
Base* b2 = static_cast<Base*>(&d1);
return 0;
}
Моя первая мысль "не, ну так конечно нельзя". А кто-то менее опытный этого не заметит. Это раз. Два — когда у тебя в коде тут сишные касты, там плюсовые — то это уже бардак. Нет единого подхода. Любой новый человек в проекте, либо же новичок будет путаться. Так лучше пусть он будет видеть, что везде плюсовые касты, и будет применять его, более безопасный подход.
Еще одна мысль: сишные касты — это как топор. Ты им можешь что угодно отрубить, покромсать, оттяпать и так далее. А плюсовые касты — это набор разных специализированных инструментов, каждый под свою задачу. Используя касты ты даешь понять читателю, что ты хотел сделать этой конструкцией. Например, что вот тут ты просто убираешь const, а вот тут преобразуешь к наследуемому типу. Это делает код более читаемым и ясным -> снижает вероятность ошибки.
Ну и см. ссылки выше: самый главный аргумент все же тот самый type safety.
Здравствуйте, so5team, Вы писали:
S>Далеко не глупые люди посмотрели к тему приводит C-шный каст, внедрили в C++ специальные конструкции, которые если не делают код безопаснее, то уж точно явно отмечают подозрительные места...
тут вопрос как раз о конструкциях, которые приводят к простреленным конечностям с C и C++ кастом. Я вот как-то придумать такие конструкции с ходу не могу.
Просто "пометить подозрительные места" — мне кажется слишком слабый аргумент для ввода 4 новых слов в язык.
S>У меня нет под рукой собственной ошибки (хотя компилятор несколько раз в год бьет по рукам в попытках неправильно использовать static_cast, особенно в обобщенном коде). Но вот хороший пример из Интернета: S>
Это как раз тот редкий случай, когда плюсовый каст нужен. Но у меня на один такой случай есть ещё 99 случаев, когда плюсовый каст не нужен. И я не вижу смысла засирать код плюсовыми кастами, потому что, когда надо будет, проблемные места будет не найти и-за того, что плюсовые касты использовались на каждый чих
Здравствуйте, Marty, Вы писали:
M>Понимаю, тебе просто западло привести их ещё раз. Или, может, ты боишься их приводить, чтобы они не были разбиты за явной несостоятельностью, ведь это бы ударило и по тебе, как принесшему их? Да не, бред какой-то
Я просто старенький уже, память плохая. Не запоминаю те места, в которых наступал на разбросанные другими людьми грабли, как и не могу в точности вспомнить каждый раз, когда компилятор бьет меня по рукам, когда я пишу невалидный static_cast или reinterpret_cast. Уж простите мне мой склероз.
Одна из самых паршивых вещей, которая происходит с Си-кастами, это наплевательство на константность. Т.е. сделать вот так:
можно запросто.
Но даже на reinterpret_cast<unsigned char *>(in) компилятор будет ругаться.
S>>PS. Ну какой наброс, такие и последствия.
M>Да нет, Ты уже которое сообщение изворачиваешься, ничего конкретного не сказав
Кто-то в интернете написал, что можно переходить проезжую часть на красный свет. Когда ему ответили, что таких переходящих можно сразу прямиком на кладбище увозить, он посетовал, что ничего конкретного не говорят, изворачиваются.
DP>Еще одна мысль: сишные касты — это как топор. Ты им можешь что угодно отрубить, покромсать, оттяпать и так далее. А плюсовые касты — это набор разных специализированных инструментов, каждый под свою задачу. Используя касты ты даешь понять читателю, что ты хотел сделать этой конструкцией. Например, что вот тут ты просто убираешь const, а вот тут преобразуешь к наследуемому типу. Это делает код более читаемым и ясным -> снижает вероятность ошибки.
Это делает код более захламлённым => менее читаемым и ясным.
Представь себе, что код ещё и какую-то логику делает, а не только типы преобразовывает.
Здравствуйте, Marty, Вы писали:
M>В целом, плюсовыми кастами пользуюсь иногда, когда надо явно сделать какое-то нетривиальное преобразование в критичном месте, и поиск таких мест по маске "_cast<" и выдаёт немного.
Для указателей использую плюсовые касты, для арифметики конструкторы.
Сишные касты для указателнй слишком жестоки, а для арифметических типов многовато скобок получается — он вам не Лисп!
M>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
Здравствуйте, Marty, Вы писали:
M> Индексы в массивах и векторах — хочет size_t, а у меня часто int. Или исторически так сложилось, или требования какого-то API, или просто было пофигу, потому что я знаю, что у меня массив вообще никогда не вырастет до такого размера, чтобы знаковый бит стал проблемой (например, вектор из созданных HPEN/HBRUSH — система ляжет раньше, чем я только начну приближаться к проблемному размеру), и, часто, при этом, этот индекс если меньше нуля, то это инвалидный индекс, и я всегда проверяю инвалидность.
Здравствуйте, Alekzander, Вы писали:
A>Здравствуйте, Marty, Вы писали:
M>>Например, я убираю возможную знаковость у char переменной
A>Для чего? Любой практически полезный сценарий надо прописывать в явном виде, с if'ами, а не неявно через типопреобразование.
A>Если я правильно понял, тебе лень нормально написать bound checking, и ты заменил его кастами.
Бывают случаи, когда бинарные данные приходят тупо в виде const char* или std::string, а собрать их нужно, скажем, в std::vector<std::uint8_t>. Повлиять на источник данных нельзя, это может быть сторонняя библиотека или кусок кода, написанный 100500 лет назад.
Здравствуйте, so5team, Вы писали:
S>За использование сишного каста в C++ном коде нужно отрывать руки, а затем выгонять на мороз (ну или в обратной последовательности).
Евангелизм как он есть.
Здравствуйте, so5team, Вы писали:
M>>ОбсудиПохоливарим?
S>За использование сишного каста в C++ном коде нужно отрывать руки, а затем выгонять на мороз (ну или в обратной последовательности).
S>Любителям оправдываться по типу "ну тут же все очевидно и так проще, да и писать меньше" для лучшего усвоения еще и ноги прострелить.
S>И нечего тут холиварить.
Я так понимаю, аргументов не будет, ты сразу на расстрел отводишь за такие вопросы?
Здравствуйте, netch80, Вы писали:
M>>Припоминаю аргументы: M>>1) Удобно искать cast'ы — но если на каждый чих их использовать, то любой поиск выдаст тонно-километры этих кастов. А если мы знаем точно, к какому типу ищем каст, то не всё ли равно, что искать — либо "_cast<TYPE>(", либо "(TYPE)"? M>>2) Сишный каст универсален, и беспринципно беспощаден — ну это есть, да, надо думать иногда, стоит ли использовать сишный каст или в узком месте использовать специализированный плюсовый каст. Может, просто стоит в код стайлах писать, в каких случаях надо использовать плюсовые касты, подразумевая, что в остальных случаях можно обойтись сишным кастом?
N>Видишь, ты и сам всё знаешь.
Это довольно спорные аргументы
M>>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
M>>ОбсудиПохоливарим?
N>Ну если не обсуждать, что у C|C++ вообще надо менять не кран, а всю систему, то обсуждать и нечего
Ну, если тебе не нравится C++, может, тебе просто сменить язык, а не пытаться менять язык?
Здравствуйте, Marty, Вы писали:
S>>Ну и вопрос: зачем усложнять себе жизнь?
M>Собери свой проект с /Wall /WX, удивись, сколько варнингов будет
Спасибо, но мы и так, по возможности, живем с -pedantic -Wall и даже -Weverything, а когда позволяют зависимости, то еще и с -Werror. Но зависимости далеко не всегда такую роскошь позволяют
M>в простейших битовых выражениях,
Во-первых, откуда там?
M>расставь там плюсовые касты и попробуй это прочитать
Во-вторых, если у вас C++ помоложе C++11, то можно же использовать локальные функции. Вот, из реального проекта (комментарии изъяты):
Здравствуйте, Marty, Вы писали:
M>Вот и я про что. Ну, grep static_cast, и что? Оно выдаст килотонны этих кастов, и накуя это нужно? А каст к конкретному типу можно и в случае сишного каста найти
Если миллионы, то следующая напрашивающаяся команда — rm -rf
Ну я если их немного, так можно их всех оптом поискать и посмотреть, так ли уж они нужны.
Но в общем и целом, если ты мое личное мнение спросишь, я не вижу в сишном касте большого греха. Особенно если учесть, что мой рабочий язык — Си, не C++
Здравствуйте, so5team, Вы писали:
S>>>Вот, из реального проекта (комментарии изъяты): BFE>>жуть.
S>Нам очень важно ваше мнение, не сдерживайтесь. Оно имеет особую ценность в свете вот этого комментария: http://rsdn.org/forum/cpp/8570137.1
Вообще да, лично я согласен с B0FEE664, код максимально ужасен. Возможно, он всё формальные проверки и проходит, но он написан не для человека, который будет его поддерживать
Здравствуйте, so5team, Вы писали: S>Чтобы сделать разговор более предметным, три вопроса к вам: S>1. У вас такой же список претензий/замечаний/вопросов/домыслов, как и у B0FEE664? Или же есть что добавить?
Я не вчитывался в смысл, потому что от одного оформления у меня кровь из глаз пошла
S>2. Если, по вашему мнению, код "максимально ужасен", то что могло бы его "исправить" (опять же по вашему мнению)?
Для начала — нормально оформить
S>3. Могли бы вы, для симметрии, привести фрагмент своего кода с кастами, дабы проиллюстрировать как должен выглядеть нормально написанный вами код?
A>Это делает код более захламлённым => менее читаемым и ясным.
Не согласен. Как раз наоборот — я написал об этом — становится явно видна логика кода, что именно ты делаешь этим преобразованием.
A>Представь себе, что код ещё и какую-то логику делает, а не только типы преобразовывает.
Касты — это крайне редкая операция, которую применяется лишь в крайних случаях. По возможности их стоит избегать. В языке полно других способов выразиться. Идеология языка объектно-ориентированная. Вот и надо мыслить объектами, их взаимодействием и операциями над ними.
Здравствуйте, Marty, Вы писали:
S>>1. У вас такой же список претензий/замечаний/вопросов/домыслов, как и у B0FEE664? Или же есть что добавить?
M>Я не вчитывался в смысл, потому что от одного оформления у меня кровь из глаз пошла
Экспертиза уровня Бох: что делается не знаю, как делается не вникал, увидел непривычное оформление и сразу вердикт "код ужасен".
Это, блин, какой-то детский сад, младшая ясельная группа.
S>>2. Если, по вашему мнению, код "максимально ужасен", то что могло бы его "исправить" (опять же по вашему мнению)?
M>Для начала — нормально оформить
Не постесняюсь спросить: а вы что, большую часть времени только с собственным кодом работаете? В чужой вообще не заглядываете?
Мне приходилось видеть и использовать разные стили оформления, так что на внешний вид, по большей части, пофиг, если только аффтары не злоупотребляют строками по 150+ символов длинной и функциями по 100+ строк.
S>>3. Могли бы вы, для симметрии, привести фрагмент своего кода с кастами, дабы проиллюстрировать как должен выглядеть нормально написанный вами код?
M>ЗЫ, хотя, вот, лови: M>#include <iostream> M>#include <iomanip> M>#include <fstream>
... M>#include <random>
Список инклюдов доставляет. Неприятный такой звоночек.
M>int main( int argc, char* argv[] ) M>{ M> UMBA_USED(argc); M> UMBA_USED(argv);
Выглядит так, как будто вы 30 лет назад программировать учились. Я про UBMA_USED для аргументов функции, на которые забили.
M> if (w32.size()>10) M> { M> auto mid = w32.size()/2; M> std::shuffle(w32.begin()+ (std::ptrdiff_t)1u, w32.begin()+(std::ptrdiff_t)mid, g); M> std::shuffle(w32.begin()+(std::ptrdiff_t)mid, w32.end() -(std::ptrdiff_t)2u , g); M> } M> else M> { M> std::shuffle(w32.begin()+(std::ptrdiff_t)1, w32.end()-(std::ptrdiff_t)2, g); M> }
Я, наверное, чего-то глобально не понимаю, но зачем это все?
Вот, по мотивам вашего кода минималистичный пример:
#include <algorithm>
#include <string>
#include <vector>
#include <random>
int main()
{
std::mt19937 g{ std::random_device{}() };
std::vector< std::string > words;
for( auto & w : words )
{
auto w32 = w;
if( w32.size() > 10u )
{
const auto mid = static_cast<std::ptrdiff_t>( w32.size() / 2 );
std::shuffle( w32.begin() + 1, w32.begin() + mid, g );
std::shuffle( w32.begin() + mid, w32.end() - 2, g );
}
}
}
Компилируем:
tmp\rsdn_cpp_20230729>cl -EHsc -WX -Wall -std:c++17 t1.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32532 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
t1.cpp
Microsoft (R) Incremental Linker Version 14.36.32532.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:t1.exe
t1.obj
Все. Никакого обилия кастов, не говоря уже про Си-шные касты.
Такое ощущение, что вы даже не дали себе труда подумать о том, что происходит. Вы явно используете литерал беззнакового типа, но вам-то нужен знаковый std::ptrdiff_t, поэтому вы недолго думая херачите Си-шный каст и получаете угребищный (std::ptrdiff_t)1u там, где достаточно всего лишь обычной единички.
S>Пока имя аргумента закоментировано, вы его не можете использовать. И не получаете никаких предупреждений от компилятора. S>Если аргумент вам таки нужен, то вы раскоментируете его имя. Если при этом забыли поиспользовать, то компилятор вам об этом напоминает.
В прототипе одно, в реализации — другое?
А потом мне захотелось закоментировать этот фрагмент кода. Только плюсовый комент (в консоли по ssh, ага, так пишут в яндексе, например, хотя там можно извратится и использовать VS Coe по ssh), или извращения с #if 0
S>Если же в прологе функции делать так: S>
S>int main(int argc, char ** argv) {
S> (void)argc; // Старая школа.
S> std::ignore = argv; // Можно и молодежно.
S>}
S>
S>то вы теряете вменяемые предупреждения от компилятора, если забыли использовать параметр в коде.
Я ничего не теряю, потому что USED добавляю, когда всё уже реализовано
Ситуации, когда забыл использовать параметр — ни разу не встречал. Обычно параметры всё таки осознанно добавляются, и не используются обычно, когда есть какой-то общий интерфейс, и в его частичной реализации необходимости части параметров нет
M>>Да, не трудился. На автомате
S>О том и речь. Обилие кастов в коде -- это явный знак, что где-то что-то недодумано.
Здравствуйте, Marty, Вы писали:
S>>А вообще знаете что? Еблюбитесь как хотите, тормоза и правила дорожного движения же придумали трусы.
M>Ну, как я и говорил, внятных аргументов у тебя нет, только демагогия про ПДД
С моей колокольни это видно так: научить можно лишь того, что хочет научится.
M>А я не просил меня учить, я просил внятных аргументов на тему того, что сишный каст — абсолютное зло. Ты их предоставлять не хочешь, всё время на ПДД съехать пытаешься
Да нет же: сишный каст — это НЕ абсолютное зло. И имеет право на существование. Но так как в плюсах итак дохрена мест, в которых можно отстрелить себе ноги, лучше сводить риски к минимуму. Опять же: это всего лишь рекомендации, дальше уж каждый делает как считает нужным.
Здравствуйте, Marty, Вы писали:
M>А я не просил меня учить, я просил внятных аргументов на тему того, что сишный каст — абсолютное зло. Ты их предоставлять не хочешь, всё время на ПДД съехать пытаешься
Абсолютного зла в IT не бывает, это уже чисто твой полемический задор. Есть зло разного уровня зловредности.
Сишные касты 1) заменяются на плюсовые без проблем, и в ~90% случаев каждый — одним кастом. 2) Грепабельны. Этого достаточно, чтобы форсировать их применение для большинства случаев.
Ну и, естественно, нужен какой-то механизм оставлять сишные с явной пометкой "Here be dragons", как же без этого. Но полиси типа "допускать такое только когда убедишь коллегу-ревьюера" вполне эффективно.
Здравствуйте, netch80, Вы писали:
S>>О том и речь. Обилие кастов в коде -- это явный знак, что где-то что-то недодумано.
N>Только в куче случаев это недодумка самого языка. Начиная с обсуждённых тут плясок вокруг размерности и знаковости типов.
Я так понимаю, что аргумент защитников Си-шных кастов состоит в том, что когда приходится делать касты (как из-за недодумок языка, так и по другим причинам), то Си-шный каст может быть предпочтительным из-за того, что писать нужно меньше, чем при использовании static_cast/const_cast/reinterpret_cast. И вот с этим я не согласен в том плане, что язык уже предоставляет инструменты для уменьшения синтаксического оверхэда. И если даже касты мы вынуждены применять из-за недодумок языка, то у нас есть возможности написать код так, чтобы совсем уж ужасно он не выглядел.
Здравствуйте, so5team, Вы писали: BFE>>Рад, что вы ждёте конструктивной критики. S>Для конструктивной нужно, чтобы критик понимал предмет разговора. Так что нет, не жду.
Т.е. вы думаете, что ваш код никто не поймёт? BFE>>Похоже, что image_bytes объявлен как указатель на const, если это так — то это зачем? S>Да, это const std::byte*, приходящий извне.
Но зачем нарушать контракт про "const"? BFE>>Почему обращение к fresh_frame->data по индексу, а не по итератору? S>Потому что это Си-ный массив. Имитировать итераторы через вызовы std::begin/end не вижу смысла.
Почему "эмитировать"? То, что в данном случае итератор — это указатель, ничего не меняет. BFE>>Где assert(std::size(fresh_frame->data) == std::size(data_plane_sizes))? S>Во-первых, assert-ы -- это развлечение для тех, кто нуждается в песочнице под названием debug mode. В release они не работают, поэтому для меня они бесполезны.
Это дело вкуса. То, что assert-ы не работают в release — опасное заблуждение. S>Во-вторых, это гарантируется в принципе Upd: гарантируется, что (std::size(fresh_frame->data) >= std::size(data_plane_sizes)). By design, так сказать.
Если By design, то где static_assert? BFE>>Зачем столько вызовов ptr_cast? Не проще ли завести один указатель? S>Потому что исходный указатель, image_bytes, постоянно изменяется (это даже в приведенном кусочке видно). А по ходу дела его приходится приводить к std::uint8_t* (да, без const, такова специфика используемой внешней библиотеки). Можно было бы, конечно, вести сразу два указателя, image_bytes и второй, который имеет тип std::uint8_t. Но какой смысл дублировать одно и то же?
Чтобы не полагаться на оптимизатор. BFE>>Уверены, что []( void *, std::uint8_t * ) -> void {} не ведёт к утечке памяти? (>24 байта за вызов) S>Мне сложно представить откуда здесь может взяться утечка, т.к. этот фрагмент -- это получение указателя на функцию, которая ничего не делает. Данный указатель требуется для av_buffer_create в качестве "деаллокатора".
А то я не вижу! S>Но т.к. деаллокатор здесь не нужен, то и функция пуста.
Вот и хотелось бы понять почему деаллокатор не нужен.
Вот в этой строчке заказ памяти AVBuffer *buf = av_mallocz(sizeof(*buf));:
AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
void (*free)(void *opaque, uint8_t *data),
void *opaque, int flags)
{
AVBufferRef *ret;
AVBuffer *buf = av_mallocz(sizeof(*buf)); // тут заказали память под AVBuffer if (!buf)
return NULL;
ret = buffer_create(buf, data, size, free, opaque, flags);
if (!ret) {
av_free(buf);
return NULL;
}
return ret;
}
смотрим дальше:
static AVBufferRef *buffer_create(AVBuffer *buf, uint8_t *data, size_t size,
void (*free)(void *opaque, uint8_t *data),
void *opaque, int flags)
{
AVBufferRef *ref = NULL;
buf->data = data;
buf->size = size;
buf->free = free ? free : av_buffer_default_free; // т.к. free не NULL, то она будет использована вместо av_buffer_default_free
buf->opaque = opaque;
atomic_init(&buf->refcount, 1);// очевидно подсчёт числа "ссылок", т.о. ниже идёт имитация intrusive смарт указателя
buf->flags = flags;
ref = av_mallocz(sizeof(*ref)); // заказали память под "ссылку"if (!ref)
return NULL;
ref->buffer = buf; // в "ссылку" положили указатель на объект AVBuffer. это, впрочем, не важно.
ref->data = data;
ref->size = size;
return ref;
}
Таким образом в av_mallocz заказывается память под объект типа AVBufferRef, но вот освобождения этой памяти нет. Следовательно: либо это утечка памяти, либо вы как-то подменили av_mallocz, но если посмотреть:
Здравствуйте, B0FEE664, Вы писали:
S>>Но тут засада, в AVBuffer указатель неконстантный. BFE>Вот я тормоз! BFE>Я правильно понимаю, что данные на самом деле не меняются, а просто в библиотеке не отличают константный буфер от неконстантного?
Начитался тут тем про MSVC /Wall /WX, и решил тоже попробовать.
Повылезала куча всего, но в основном signed/unsigned mismatch, и вот это вот всё. Индексы в массивах и векторах — хочет size_t, а у меня часто int. Или исторически так сложилось, или требования какого-то API, или просто было пофигу, потому что я знаю, что у меня массив вообще никогда не вырастет до такого размера, чтобы знаковый бит стал проблемой (например, вектор из созданных HPEN/HBRUSH — система ляжет раньше, чем я только начну приближаться к проблемному размеру), и, часто, при этом, этот индекс если меньше нуля, то это инвалидный индекс, и я всегда проверяю инвалидность. Или битовые операторы и операторы сдвига — они, как оказалось, возвращают int/int64, а не тот тип, который изначально участвует в выражении. Полечил сишными кастами. Если бы лечить static_cast'ами, это каждое выражение становится пипец каким ужасным и нечитаемыми.
В целом, плюсовыми кастами пользуюсь иногда, когда надо явно сделать какое-то нетривиальное преобразование в критичном месте, и поиск таких мест по маске "_cast<" и выдаёт немного.
Почему сишным кастом пользоваться плохо?
Например, я убираю возможную знаковость у char переменной и конвертирую её в unsigned как-то так:
auto u = (unsigned)(std::uint8_t)ch;
Но можно и так:
auto u = static_cast<unsigned>(static_cast<std::uint8_t>(ch));
Во втором случае выражение гораздо менее наглядно, приходится скрипеть мозгами, чтобы понять, что там происходит.
Припоминаю аргументы:
1) Удобно искать cast'ы — но если на каждый чих их использовать, то любой поиск выдаст тонно-километры этих кастов. А если мы знаем точно, к какому типу ищем каст, то не всё ли равно, что искать — либо "_cast<TYPE>(", либо "(TYPE)"?
2) Сишный каст универсален, и беспринципно беспощаден — ну это есть, да, надо думать иногда, стоит ли использовать сишный каст или в узком месте использовать специализированный плюсовый каст. Может, просто стоит в код стайлах писать, в каких случаях надо использовать плюсовые касты, подразумевая, что в остальных случаях можно обойтись сишным кастом?
Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
Здравствуйте, reversecode, Вы писали:
R>вот они последователи шмыги, которые ни разу не бывали на собеседованиях
Я в яндекс дзене немного посидел, там скукота, перекладывание из одной шляпы в другую. В НИИ я примерно тем же занимался, но там было веселее — там раз в несколько месяцев мой робот начинал летать/ползать, это драйвило.
Да, даже в НИИ в код стайле было прописано использование строго только плюсовых кастов. И нигде я не видел внятных обоснований для этого.
ЗЫ А ты зачем Шмыгу обижаешь, твой же побратим, не?
Здравствуйте, reversecode, Вы писали:
R>вот они последователи шмыги, которые ни разу не бывали на собеседованиях
Кстати, ты бы мог, как сознательный украинец, взять шмыгу под свой патронаж, помочь побратиму, а заодно вы бы схлопнулись и аннигилировали, как частица и античастица, очистив профильные форумы от своего присутствия
Здравствуйте, Marty, Вы писали:
M>Например, я убираю возможную знаковость у char переменной
Для чего? Любой практически полезный сценарий надо прописывать в явном виде, с if'ами, а не неявно через типопреобразование.
Если я правильно понял, тебе лень нормально написать bound checking, и ты заменил его кастами.
M>ОбсудиПохоливарим?
Я следую прямо противоположному правилу: кроме сишных кастов ничего не использовать, чтобы было меньше говнокода. Логика очень простая и железобетонная: ЛЮБОЙ КАСТ ЭТО СИТУАЦИЯ, КОГДА *ТЫ* ЗНАЕШЬ О НАСТОЯЩЕМ ТИПЕ, А КОМПИЛЯТОР — ПОЧЕМУ-ТО НЕТ. Таких ситуаций надо избегать, потому, что они говорят о неправильной архитектуре и/или неверно написанном коде. Есть, конечно, ситуации, когда имеешь дело с историческими артефактами типа WinAPI, но там и Си-каста хватает.
Здравствуйте, Marty, Вы писали:
M>Например, я убираю возможную знаковость у char переменной и конвертирую её в unsigned как-то так: M>
M>auto u = (unsigned)(std::uint8_t)ch;
M>
Achtung! unsigned это unsigned int, а не unsigned char. Тут один байт в четыре превращается.
M>Но можно и так: M>
M>auto u = static_cast<unsigned>(static_cast<std::uint8_t>(ch));
M>
static_cast, dynamic_cast, reinterpret_cast это в первую очередь инструменты для преобразования указателей базовых классов в унаследованные с контролем или в случае reinterpret_cast явное указание, что не в наследуемый.
Хотя в рекомендациях пишут, что static_cast можно и нужно использовать даже для встроенных типов.
Здравствуйте, so5team, Вы писали:
M>>>Например, я убираю возможную знаковость у char переменной
A>>Для чего? Любой практически полезный сценарий надо прописывать в явном виде, с if'ами, а не неявно через типопреобразование.
A>>Если я правильно понял, тебе лень нормально написать bound checking, и ты заменил его кастами.
S>Бывают случаи, когда бинарные данные приходят тупо в виде const char* или std::string, а собрать их нужно, скажем, в std::vector<std::uint8_t>. Повлиять на источник данных нельзя, это может быть сторонняя библиотека или кусок кода, написанный 100500 лет назад.
Я же написал, что иногда приходится иметь дело с историческими артефактами, но там и Си-каста хватает. Речь идёт о том, что всё, что сложнее, лично для меня детектор.
Здравствуйте, Marty, Вы писали:
M>Припоминаю аргументы: M>1) Удобно искать cast'ы — но если на каждый чих их использовать, то любой поиск выдаст тонно-километры этих кастов. А если мы знаем точно, к какому типу ищем каст, то не всё ли равно, что искать — либо "_cast<TYPE>(", либо "(TYPE)"? M>2) Сишный каст универсален, и беспринципно беспощаден — ну это есть, да, надо думать иногда, стоит ли использовать сишный каст или в узком месте использовать специализированный плюсовый каст. Может, просто стоит в код стайлах писать, в каких случаях надо использовать плюсовые касты, подразумевая, что в остальных случаях можно обойтись сишным кастом?
Видишь, ты и сам всё знаешь.
M>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
M>ОбсудиПохоливарим?
Ну если не обсуждать, что у C|C++ вообще надо менять не кран, а всю систему, то обсуждать и нечего
Здравствуйте, Pzz, Вы писали:
M>>Почему сишным кастом пользоваться плохо?
Pzz>Потому, что grep static_cast сказать можно, а в случае сишного каста грепать нечего.
Pzz>Но это не очень сильный аргумент IMHO
Вот и я про что. Ну, grep static_cast, и что? Оно выдаст килотонны этих кастов, и накуя это нужно? А каст к конкретному типу можно и в случае сишного каста найти
Здравствуйте, so5team, Вы писали:
S>>>За использование сишного каста в C++ном коде нужно отрывать руки, а затем выгонять на мороз (ну или в обратной последовательности). K>>Евангелизм как он есть.
S>Или слишком много набитых в C++ шишек, которых можно было бы избежать, если бы программисты думали о том, как написать нормальный код, а не о том, чтобы сэкономить несколько символов.
S>Далеко не глупые люди посмотрели к тему приводит C-шный каст, внедрили в C++ специальные конструкции, которые если не делают код безопаснее, то уж точно явно отмечают подозрительные места... Но нет, всегда есть кто-то шибко умный, который скажет, что у него-то уж точно все нормально.
У меня очень, очень много рабочего кода, написанного с сишными кастами. В том числе коммерческий (был, сейчас скис, спрос упал) проект, и плагин к Far'у. И это всё как-то работает
Здравствуйте, so5team, Вы писали:
M>>Я так понимаю, аргументов не будет, ты сразу на расстрел отводишь за такие вопросы?
S>Все аргументы уже многократно были высказаны множеством людей во множестве мест. Кто до сих пор не внял -- на экзекуцию.
Понимаю, тебе просто западло привести их ещё раз. Или, может, ты боишься их приводить, чтобы они не были разбиты за явной несостоятельностью, ведь это бы ударило и по тебе, как принесшему их? Да не, бред какой-то
S>PS. Ну какой наброс, такие и последствия.
Да нет, Ты уже которое сообщение изворачиваешься, ничего конкретного не сказав
Здравствуйте, so5team, Вы писали:
S>Одна из самых паршивых вещей, которая происходит с Си-кастами, это наплевательство на константность. Т.е. сделать вот так: S>
Можно. И что? Можно быстро выпить 10 литров воды и умереть от отравления H2O.
S>Но даже на reinterpret_cast<unsigned char *>(in) компилятор будет ругаться.
А на const_cast — не будет. И?
M>>Да нет, Ты уже которое сообщение изворачиваешься, ничего конкретного не сказав
S>Кто-то в интернете написал, что можно переходить проезжую часть на красный свет. Когда ему ответили, что таких переходящих можно сразу прямиком на кладбище увозить, он посетовал, что ничего конкретного не говорят, изворачиваются.
Здравствуйте, so5team, Вы писали:
M>>Можно. И что?
S>А то, что исходные данные не просто так константы. Но компилятор не поможет вам защититься от того, что вы их модифицируете. А мог бы.
S>Искать подобную ошибку (например, когда в многопоточном коде один общий буфер с константными данными разделили между несколькими тредами, а один из них начал эти данные менять по ходу дела) можно будет долго и больно.
S>>>Но даже на reinterpret_cast<unsigned char *>(in) компилятор будет ругаться.
M>>А на const_cast — не будет. И?
S>const_cast вам придется написать явно. И тут есть два сценария, когда вы написали reinterpret_cast без const_cast и получили по рукам от компилятора:
S>1. Вы не обратили внимания на то, что исходный указатель константный (а может и не знали об этом, если все происходит в обобщенном коде, где какой-нибудь Src_Type может быть как const-указателем, так и не-const). Компилятор указал вам на это и вы поняли, что делаете что-то не то. И меняете либо входящий тип (чтобы он не было константным), либо меняете свой код, чтобы обходиться константным указателем. Либо пишете const_cast и молитесь на отсутствие UB.
S>2. Вы дописали const_cast не приходя в сознание. Ну хочет компилятор, ну на тебе. В этом случае хотя бы это место будет проще найти, т.к. const_cast-ы быстрее бросаются в глаза да и ищутся проще, чем Си-касты.
S>Т.е. даже в худшем случае вы получаете больше бенефитов, чем от Си-каста.
Ты описал ситуацию, которая ровно ноль раз у меня в практике встречалась
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Marty, Вы писали:
M>> Индексы в массивах и векторах — хочет size_t, а у меня часто int. Или исторически так сложилось, или требования какого-то API, или просто было пофигу, потому что я знаю, что у меня массив вообще никогда не вырастет до такого размера, чтобы знаковый бит стал проблемой (например, вектор из созданных HPEN/HBRUSH — система ляжет раньше, чем я только начну приближаться к проблемному размеру), и, часто, при этом, этот индекс если меньше нуля, то это инвалидный индекс, и я всегда проверяю инвалидность.
TB>Добро пожаловать в клуб TB>https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1428r0.pdf
Здравствуйте, so5team, Вы писали:
S>Нам очень важно ваше мнение, не сдерживайтесь.
Рад, что вы ждёте конструктивной критики.
Похоже, что image_bytes объявлен как указатель на const, если это так — то это зачем? Чтобы затруднить чтение кода?
А если image_bytes не указатель на const, то зачем тогда const у параметра ptr_cast?
Почему обращение к fresh_frame->data по индексу, а не по итератору?
Где assert(std::size(fresh_frame->data) == std::size(data_plane_sizes))?
Зачем столько вызовов ptr_cast? Не проще ли завести один указатель?
Уверены, что []( void *, std::uint8_t * ) -> void {} не ведёт к утечке памяти? (>24 байта за вызов)
S>Оно имеет особую ценность в свете вот этого комментария: http://rsdn.org/forum/cpp/8570137.1
Здравствуйте, B0FEE664, Вы писали:
S>>Нам очень важно ваше мнение, не сдерживайтесь. BFE>Рад, что вы ждёте конструктивной критики.
Для конструктивной нужно, чтобы критик понимал предмет разговора. Так что нет, не жду.
BFE>Похоже, что image_bytes объявлен как указатель на const, если это так — то это зачем?
Да, это const std::byte*, приходящий извне.
BFE>Почему обращение к fresh_frame->data по индексу, а не по итератору?
Потому что это Си-ный массив. Имитировать итераторы через вызовы std::begin/end не вижу смысла.
BFE>Где assert(std::size(fresh_frame->data) == std::size(data_plane_sizes))?
Во-первых, assert-ы -- это развлечение для тех, кто нуждается в песочнице под названием debug mode. В release они не работают, поэтому для меня они бесполезны.
Во-вторых, это гарантируется в принципе Upd: гарантируется, что (std::size(fresh_frame->data) >= std::size(data_plane_sizes)). By design, так сказать.
BFE>Зачем столько вызовов ptr_cast? Не проще ли завести один указатель?
Потому что исходный указатель, image_bytes, постоянно изменяется (это даже в приведенном кусочке видно). А по ходу дела его приходится приводить к std::uint8_t* (да, без const, такова специфика используемой внешней библиотеки). Можно было бы, конечно, вести сразу два указателя, image_bytes и второй, который имеет тип std::uint8_t. Но какой смысл дублировать одно и то же?
BFE>Уверены, что []( void *, std::uint8_t * ) -> void {} не ведёт к утечке памяти? (>24 байта за вызов)
Мне сложно представить откуда здесь может взяться утечка, т.к. этот фрагмент -- это получение указателя на функцию, которая ничего не делает. Данный указатель требуется для av_buffer_create в качестве "деаллокатора". Но т.к. деаллокатор здесь не нужен, то и функция пуста.
Надеюсь, вы в курсе, что C++ная лямбда без захвата автоматически может приводится к указателю на функцию с таким же прототипом:
int main() {
int (*pf)(char, long) = nullptr;
auto dummy = [](char, long) -> int { return 42; };
pf = dummy;
return (*pf)('x', 100500);
}
M>>Спасибо, кэп, я в курсе. Поэтому я сначала кастую к unsigned типу той же размерности, и только потом к unsigned int'у
BFE>Чтобы что? Чтобы напечать 'no' в следующем коде, если char — это signed?: BFE>
BFE> const char ch = 254;
BFE> const auto u = (unsigned)(std::uint8_t)ch;
BFE> if ( ch == u )
BFE> std::cout << "yes\n";
BFE> else
BFE> std::cout << "no\n";
BFE>
Откуда-то снаружи приходят данные в виде char'ов, какая-то упаковка протокола, например. По сути — данные беззнаковые, но библиотека выдаёт их в виде char. У меня же эти данные могут быть дополнены новыми значениями. Поэтому сначала привожу к беззнаковому типу того же размера, а потом расширяю размер. Если сразу размер расширить, могут быть сюрпризы
Здравствуйте, Marty, Вы писали:
M>Вообще да, лично я согласен с B0FEE664, код максимально ужасен. Возможно, он всё формальные проверки и проходит, но он написан не для человека, который будет его поддерживать
Убедительно, да (на самом деле нет).
Чтобы сделать разговор более предметным, три вопроса к вам:
1. У вас такой же список претензий/замечаний/вопросов/домыслов, как и у B0FEE664? Или же есть что добавить?
2. Если, по вашему мнению, код "максимально ужасен", то что могло бы его "исправить" (опять же по вашему мнению)?
3. Могли бы вы, для симметрии, привести фрагмент своего кода с кастами, дабы проиллюстрировать как должен выглядеть нормально написанный вами код?
Здравствуйте, Marty, Вы писали:
M>>>Припоминаю аргументы: M>>>1) Удобно искать cast'ы — но если на каждый чих их использовать, то любой поиск выдаст тонно-километры этих кастов. А если мы знаем точно, к какому типу ищем каст, то не всё ли равно, что искать — либо "_cast<TYPE>(", либо "(TYPE)"? M>>>2) Сишный каст универсален, и беспринципно беспощаден — ну это есть, да, надо думать иногда, стоит ли использовать сишный каст или в узком месте использовать специализированный плюсовый каст. Может, просто стоит в код стайлах писать, в каких случаях надо использовать плюсовые касты, подразумевая, что в остальных случаях можно обойтись сишным кастом?
N>>Видишь, ты и сам всё знаешь.
M>Это довольно спорные аргументы
Я вижу в них безусловно ненулевую силу.
M>>>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
M>>>ОбсудиПохоливарим?
N>>Ну если не обсуждать, что у C|C++ вообще надо менять не кран, а всю систему, то обсуждать и нечего
M>Ну, если тебе не нравится C++, может, тебе просто сменить язык, а не пытаться менять язык?
Здравствуйте, Marty, Вы писали:
M>>>>>auto u = (unsigned)(std::uint8_t)ch; M>>>Спасибо, кэп, я в курсе. Поэтому я сначала кастую к unsigned типу той же размерности, и только потом к unsigned int'у M>Откуда-то снаружи приходят данные в виде char'ов, какая-то упаковка протокола, например. По сути — данные беззнаковые, но библиотека выдаёт их в виде char. У меня же эти данные могут быть дополнены новыми значениями. Поэтому сначала привожу к беззнаковому типу того же размера, а потом расширяю размер. Если сразу размер расширить, могут быть сюрпризы
255&ch работает универсально в рамках всех версий С|С++ c тем же успехом и прозрачнее (IMHO).
Хотя я бы подпёр юнит-тестом просто чтобы показать коллегам, что результат проверен.
S>Не постесняюсь спросить: а вы что, большую часть времени только с собственным кодом работаете? В чужой вообще не заглядываете? S>Мне приходилось видеть и использовать разные стили оформления, так что на внешний вид, по большей части, пофиг, если только аффтары не злоупотребляют строками по 150+ символов длинной и функциями по 100+ строк.
На последнем месте очень вменяемый код стайл, а так — да, заглядываю в разнве библиотеки. Как-то в clang заглядывал, все глаза выплакал
S>>>3. Могли бы вы, для симметрии, привести фрагмент своего кода с кастами, дабы проиллюстрировать как должен выглядеть нормально написанный вами код?
M>>ЗЫ, хотя, вот, лови: M>>#include <iostream> M>>#include <iomanip> M>>#include <fstream> S>... M>>#include <random>
S>Список инклюдов доставляет. Неприятный такой звоночек.
Это один из тестов, просто генерится из шаблона
M>>int main( int argc, char* argv[] ) M>>{ M>> UMBA_USED(argc); M>> UMBA_USED(argv);
S>Выглядит так, как будто вы 30 лет назад программировать учились. Я про UBMA_USED для аргументов функции, на которые забили.
Есть варианты лучше?
M>> if (w32.size()>10) M>> { M>> auto mid = w32.size()/2; M>> std::shuffle(w32.begin()+ (std::ptrdiff_t)1u, w32.begin()+(std::ptrdiff_t)mid, g); M>> std::shuffle(w32.begin()+(std::ptrdiff_t)mid, w32.end() -(std::ptrdiff_t)2u , g); M>> } M>> else M>> { M>> std::shuffle(w32.begin()+(std::ptrdiff_t)1, w32.end()-(std::ptrdiff_t)2, g); M>> }
S>Я, наверное, чего-то глобально не понимаю, но зачем это все?
Что именно?
S>Такое ощущение, что вы даже не дали себе труда подумать о том, что происходит. Вы явно используете литерал беззнакового типа, но вам-то нужен знаковый std::ptrdiff_t, поэтому вы недолго думая херачите Си-шный каст и получаете угребищный (std::ptrdiff_t)1u там, где достаточно всего лишь обычной единички.
Здравствуйте, DiPaolo, Вы писали:
A>>Это делает код более захламлённым => менее читаемым и ясным. DP>Не согласен. Как раз наоборот — я написал об этом — становится явно видна логика кода, что именно ты делаешь этим преобразованием.
A>>Представь себе, что код ещё и какую-то логику делает, а не только типы преобразовывает. DP>Касты — это крайне редкая операция, которую применяется лишь в крайних случаях. По возможности их стоит избегать. В языке полно других способов выразиться. Идеология языка объектно-ориентированная. Вот и надо мыслить объектами, их взаимодействием и операциями над ними.
99% кастов — это снятие/добавление знака, расширение/сужение типа. Там плюсовые касты нахрен не упёрлись. В тех случаях, когда что-то другое надо — тогда да, плюсовые касты вполне уместны
Здравствуйте, Marty, Вы писали:
M>>>int main( int argc, char* argv[] ) M>>>{ M>>> UMBA_USED(argc); M>>> UMBA_USED(argv);
S>>Выглядит так, как будто вы 30 лет назад программировать учились. Я про UBMA_USED для аргументов функции, на которые забили.
M>Есть варианты лучше?
Да. Например:
void f(int/*some_arg*/) {
}
Пока имя аргумента закоментировано, вы его не можете использовать. И не получаете никаких предупреждений от компилятора.
Если аргумент вам таки нужен, то вы раскоментируете его имя. Если при этом забыли поиспользовать, то компилятор вам об этом напоминает.
Если же в прологе функции делать так:
int main(int argc, char ** argv) {
(void)argc; // Старая школа.
std::ignore = argv; // Можно и молодежно.
}
то вы теряете вменяемые предупреждения от компилятора, если забыли использовать параметр в коде.
M>Да, не трудился. На автомате
О том и речь. Обилие кастов в коде -- это явный знак, что где-то что-то недодумано.
То я не понимаю в чем проблема. Компилятор интересует только реализация, какие имена аргументов в декларации ему фиолетово.
M>А потом мне захотелось закоментировать этот фрагмент кода. Только плюсовый комент (в консоли по ssh, ага, так пишут в яндексе, например, хотя там можно извратится и использовать VS Coe по ssh), или извращения с #if 0
Не устраивают комментарии вокруг имени, вообще уберите имя:
void f(int) {
...
}
M>Я ничего не теряю, потому что USED добавляю, когда всё уже реализовано
Ох, ё.
M>Или нет
Ох, ё-ё-ё.
А вообще знаете что? Еблюбитесь как хотите, тормоза и правила дорожного движения же придумали трусы.
Здравствуйте, so5team, Вы писали:
S>То я не понимаю в чем проблема. Компилятор интересует только реализация, какие имена аргументов в декларации ему фиолетово.
ИМена в декларации интересуют программиста, который будет поддерживать код. Держать две разные версии одного и того же — весьма неудобно
M>>А потом мне захотелось закоментировать этот фрагмент кода. Только плюсовый комент (в консоли по ssh, ага, так пишут в яндексе, например, хотя там можно извратится и использовать VS Coe по ssh), или извращения с #if 0
S>Не устраивают комментарии вокруг имени, вообще уберите имя: S>
S>void f(int) {
S> ...
S>}
Приходим к тому, что у декларации и реализации записано по разному. Ну, или можно из декларации тоже всё убрать, отличная идея.
S>А вообще знаете что? Еблюбитесь как хотите, тормоза и правила дорожного движения же придумали трусы.
Ну, как я и говорил, внятных аргументов у тебя нет, только демагогия про ПДД
Здравствуйте, so5team, Вы писали:
S>>>А вообще знаете что? Еблюбитесь как хотите, тормоза и правила дорожного движения же придумали трусы.
M>>Ну, как я и говорил, внятных аргументов у тебя нет, только демагогия про ПДД
S>С моей колокольни это видно так: научить можно лишь того, что хочет научится.
А я не просил меня учить, я просил внятных аргументов на тему того, что сишный каст — абсолютное зло. Ты их предоставлять не хочешь, всё время на ПДД съехать пытаешься
Здравствуйте, andrey.desman, Вы писали:
M>>В целом, плюсовыми кастами пользуюсь иногда, когда надо явно сделать какое-то нетривиальное преобразование в критичном месте, и поиск таких мест по маске "_cast<" и выдаёт немного.
AD>Для указателей использую плюсовые касты, для арифметики конструкторы. AD>Сишные касты для указателнй слишком жестоки, а для арифметических типов многовато скобок получается — он вам не Лисп!
На мой взгляд, абсолютно разумная позиция. К сожалению, ни кто из участников не привел таких простых и внятных аргументов
M>>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
AD>Зачем искать касты?
Ну, я не знаю, любители плюсовых кастов говорят, что их искать проще
Здравствуйте, Marty, Вы писали:
M>А я не просил меня учить
Усвоение новой информации это уже не учеба? Ну ОК, как уже было сказано, любитесь как хотите.
M>Ты их предоставлять не хочешь, всё время на ПДД съехать пытаешься
Если вы не заметили, я здесь один из двух человек (второй DiPaolo), который хоть что-то приводит в подтверждение своих слов и единственный, который делает это на конкретных примерах. Если это "предоставлять не хочет", то... Любитесь как хотите, опять же.
Здравствуйте, andrey.desman, Вы писали:
M>>Да, для удобства поиска использую именно сишный каст, а не constructor style cast вида int(...)
AD>Зачем искать касты?
Ну как же. Вот будет в интернете кто-то неправ не поверит тов.Marty, что приходится снимать константность с указателя. А ты такой "Хоп!" и простым grep-ом по исходникам обнаруживаешь места, где const_cast используется. Ну красота же!
Здравствуйте, Marty, Вы писали:
M>Или битовые операторы и операторы сдвига — они, как оказалось, возвращают int/int64, а не тот тип, который изначально участвует в выражении. Полечил сишными кастами.
Про целочисленное расширение (integral promotion) почитай
Здравствуйте, so5team, Вы писали:
S>У меня нет под рукой собственной ошибки (хотя компилятор несколько раз в год бьет по рукам в попытках неправильно использовать static_cast, особенно в обобщенном коде). Но вот хороший пример из Интернета:
Добавь reinterpret_cast
Здравствуйте, _NN_, Вы писали:
_NN>У приведения в стиле abc_cast<Type> есть преимущество в однородности синтаксиса с пользовательскими приведениями типов. _NN>Например pointer_cast
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, _NN_, Вы писали:
_NN>>У приведения в стиле abc_cast<Type> есть преимущество в однородности синтаксиса с пользовательскими приведениями типов. _NN>>Например pointer_cast
.
M>Это чем же C-style или constructor style касты неоднородны в синтаксисе с пользовательскими приведениями типов?
Тем, что нельзя «перегрузить» для примитивных типов.
К примеру lexical_cast<int> , pointer_cast<int*> .
Как это сделать в стиле конструктора ?
Можно конечно через промежуточный объект и операцию преобразования:
PointerCast<int*>(ptr) с каким-нибудь operator int*().
Но тут у нас лишние сущности получаются.
Здравствуйте, B0FEE664, Вы писали:
BFE>>>Рад, что вы ждёте конструктивной критики. S>>Для конструктивной нужно, чтобы критик понимал предмет разговора. Так что нет, не жду. BFE>Т.е. вы думаете, что ваш код никто не поймёт?
Я очень надеюсь, что критиковать код начнут после того как поймут, а не до.
BFE>>>Похоже, что image_bytes объявлен как указатель на const, если это так — то это зачем? S>>Да, это const std::byte*, приходящий извне. BFE>Но зачем нарушать контракт про "const"?
Потому, что в FFMPEG нужно иметь неконстантный указатель.
BFE>>>Почему обращение к fresh_frame->data по индексу, а не по итератору? S>>Потому что это Си-ный массив. Имитировать итераторы через вызовы std::begin/end не вижу смысла. BFE>Почему "эмитировать"? То, что в данном случае итератор — это указатель, ничего не меняет.
Потому, что мне здесь "итераторы" в каком-либо виде не нужны. Если вам кажется, что нужны, то, наверное, для этого есть какие-то доводы?
BFE>Это дело вкуса.
Скорее опыта.
BFE>То, что assert-ы не работают в release — опасное заблуждение.
Если assert-ы работают в релизе, то это уже не assert-ы.
S>>Во-вторых, это гарантируется в принципе Upd: гарантируется, что (std::size(fresh_frame->data) >= std::size(data_plane_sizes)). By design, так сказать. BFE>Если By design, то где static_assert?
В Караганде, вероятно. Потому что by design. Т.к. если в FFMPEG внезапно сделают так, что в AVFrame::data будет меньше элементов, чем в аргументе linesizes для av_image_fill_plane_sizes, то нужно будет что-то менять в консерватории. Может быть законченный параноик будет напишет (не один) static_assert чтобы защититься от такой гипотетической ситуации, но я подобной паранойей еще не страдаю.
Вот по поводу Си-шных кастов паранойя есть, т.к. там опасности не такие гипотетические.
BFE>>>Зачем столько вызовов ptr_cast? Не проще ли завести один указатель? S>>Потому что исходный указатель, image_bytes, постоянно изменяется (это даже в приведенном кусочке видно). А по ходу дела его приходится приводить к std::uint8_t* (да, без const, такова специфика используемой внешней библиотеки). Можно было бы, конечно, вести сразу два указателя, image_bytes и второй, который имеет тип std::uint8_t. Но какой смысл дублировать одно и то же? BFE>Чтобы не полагаться на оптимизатор.
Оптимизатор-то здесь причем? Или вы думаете, что на reinterpret_cast+const_cast есть какие-то накладные расходы в run-time?
BFE>>>Уверены, что []( void *, std::uint8_t * ) -> void {} не ведёт к утечке памяти? (>24 байта за вызов) S>>Мне сложно представить откуда здесь может взяться утечка, т.к. этот фрагмент -- это получение указателя на функцию, которая ничего не делает. Данный указатель требуется для av_buffer_create в качестве "деаллокатора". BFE>А то я не вижу!
Раз высказываете такие опасения, значит не видите. Или видите что-то свое.
S>>Но т.к. деаллокатор здесь не нужен, то и функция пуста. BFE>Вот и хотелось бы понять почему деаллокатор не нужен.
Потому что это деаллокатор не для AVBuffer, а для AVBuffer::data.
Вызывается этот деаллокатор здесь (через здесь):
static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
{
AVBuffer *b;
b = (*dst)->buffer;
if (src) {
**dst = **src;
av_freep(src);
} else
av_freep(dst);
if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1) {
/* b->free below might already free the structure containing *b,
* so we have to read the flag now to avoid use-after-free. */int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE);
b->free(b->opaque, b->data);if (free_avbuffer)
av_free(b);
}
}
BFE>Таким образом: вы всё ещё уверены, что здесь нет утечки памяти?
Я ни в чем на 100% не уверен, но в данном случае очень надеюсь.
Здравствуйте, so5team, Вы писали:
S>Я очень надеюсь, что критиковать код начнут после того как поймут, а не до.
Ну уж нет. Код должен читаться, а не являться ребусом.
BFE>>>>Похоже, что image_bytes объявлен как указатель на const, если это так — то это зачем? S>>>Да, это const std::byte*, приходящий извне.
Да, это я не правильный вопрос задал. Попытаюсь ещё раз. Почему извне приходить указатель на константные данные?
S>Потому, что мне здесь "итераторы" в каком-либо виде не нужны. Если вам кажется, что нужны, то, наверное, для этого есть какие-то доводы?
Вот этот цикл:
auto pImgBytes = ptr_cast( image_bytes );
auto pData = std::begin(fresh_frame->data);
for( const auto plane_size : data_plane_sizes )
{
*pData++ = pImgBytes;
pImgBytes += plane_size;
}
BFE>>То, что assert-ы не работают в release — опасное заблуждение. S>Если assert-ы работают в релизе, то это уже не assert-ы.
А что тогда?
S>В Караганде, вероятно. Потому что by design. Т.к. если в FFMPEG внезапно сделают так, что в AVFrame::data будет меньше элементов, чем в аргументе linesizes для av_image_fill_plane_sizes, то нужно будет что-то менять в консерватории. Может быть законченный параноик будет напишет (не один) static_assert чтобы защититься от такой гипотетической ситуации, но я подобной паранойей еще не страдаю.
Вот видите, для понимания вашего кода нужно знать некий внешний контекст. Т.е. гарантии того, что код будет работать лежат где-то ещё, а не в самом коде. В результате код не очень-то хорош.
S>Потому что это деаллокатор не для AVBuffer, а для AVBuffer::data.
Ага. теперь понятно.
Здравствуйте, B0FEE664, Вы писали:
S>>Я очень надеюсь, что критиковать код начнут после того как поймут, а не до. BFE>Ну уж нет. Код должен читаться, а не являться ребусом.
Код должен решать задачу. При этом (но не вместо!) он должен быть понятным, корректным, надежным, сопровождабельным и т.д.
Но код пишется для решения задачи. И так бывает, что для решения задачи нужно понимание особенностей предметной области (хотя бы на уровне знакомства с используемым инструментарием), без которого (без понимания в смысле) читабельность кода -- это очень эфемерное, но спекулятивное понятие.
BFE>>>>>Похоже, что image_bytes объявлен как указатель на const, если это так — то это зачем? S>>>>Да, это const std::byte*, приходящий извне. BFE>Да, это я не правильный вопрос задал. Попытаюсь ещё раз. Почему извне приходить указатель на константные данные?
Объективная реальность. Скажем, какие-то данные берутся из shared-memory, замапленной в память процесса в режиме read-only.
S>>Потому, что мне здесь "итераторы" в каком-либо виде не нужны. Если вам кажется, что нужны, то, наверное, для этого есть какие-то доводы? BFE>Вот этот цикл: BFE>
Можно и так. Только это дело вкуса. Объективных преимуществ ни у одного из способов нет. Процитированный мной код получился в результате эволюции, на предыдущих стадиях которых, вероятно, plane_index был более нужен именно как индекс.
BFE>>>То, что assert-ы не работают в release — опасное заблуждение. S>>Если assert-ы работают в релизе, то это уже не assert-ы. BFE>А что тогда?
Обычные run-time проверки но с самым, пожалуй, жестким способом реакции на проблему.
BFE>Вот видите, для понимания вашего кода нужно знать некий внешний контекст. Т.е. гарантии того, что код будет работать лежат где-то ещё, а не в самом коде. В результате код не очень-то хорош.
См. выше про потребность быть в предметной области.
Здравствуйте, so5team, Вы писали:
S>Объективная реальность. Скажем, какие-то данные берутся из shared-memory, замапленной в память процесса в режиме read-only.
Не, ну так не честно! Я думал речь про нормальную программу, а не про хакерство.
И всё равно не понятно, зачем вызывать cast на каждый доступ.
Здравствуйте, B0FEE664, Вы писали:
S>>Объективная реальность. Скажем, какие-то данные берутся из shared-memory, замапленной в память процесса в режиме read-only.
BFE>Не, ну так не честно! Я думал речь про нормальную программу, а не про хакерство.
Хакерство здесь не при чем. Можно смотреть на вещи так: есть подсистема A, у которой откуда-то в памяти появляются данные. Часть этих данных должна попасть к подсистеме B, но только на чтение, поэтому подсистема A отдает подсистеме B const-указатель. Далее подсистеме B нужно сформировать AVFrame, который бы использовал данные по полученному указателю. Но тут засада, в AVBuffer указатель неконстантный. Отсюда и происхождение const_cast.
При этом параллельно с подсистемой B над теми же самыми данными может работать и какая-нибудь подсистема C или D. И они так же не должны ничего по указателю менять, поэтому и к ним так же пойдет const-указатель.
BFE>И всё равно не понятно, зачем вызывать cast на каждый доступ.
Там же не зря image_bytes постоянно инкрементируется, т.к. его результирующее значение важно и используется впоследствии (за пределами процитированного фрагмента).
Посему выбор просто:
1) либо работать с исходным константным image_bytes, применяя cast только в тех местах, где реально нужен неконстантный указатель;
2) либо создать временный неконстантный указатель из image_bytes, работать с ним, а потом его значение возвращать, но уже в виде константного.
Второй вариант мне нравится сильно меньше, т.к. значительно увеличивается область, в которой живет неконстантный указатель на константные, по сути, данные.
Здравствуйте, so5team, Вы писали:
S>Но тут засада, в AVBuffer указатель неконстантный.
Вот я тормоз!
Я правильно понимаю, что данные на самом деле не меняются, а просто в библиотеке не отличают константный буфер от неконстантного?
Здравствуйте, Marty, Вы писали:
M>Собери свой проект с /Wall /WX, удивись, сколько варнингов [...]
(Внезапно!) все, кто серьезно относятся к качеству кода с дня нумер один разработки (а не постфактум, да ради прикола), уже собирают со всеми флагами "хорошего тона", и никаких удивлений у них нет.
Иногда еще даже с -Werror впридачу.
Здравствуйте, Marty, Вы писали:
M>Собери свой проект с /Wall /WX, удивись, сколько варнингов будет в простейших битовых выражениях, расставь там плюсовые касты и попробуй это прочитать
Я последние несколько гигантских проектов собирал (в том числе форк хромиума и форк Robotic OS) исключительно с параноидными настройками компилятора.
И ничего.
Сложно первые полгода, пока всё говнище от старых дореформенных разработчиков не вычистишь. Естетственно, не в одно лицо, там трудозатрат на десятки человеко-лет.
А потом — просто не говнокодишь; а линтер и тесты, (а в некоторых случаях — живые ревьюверы) тебе помогают-заставляют.
Здравствуйте, Marty, Вы писали:
M>И это всё как-то работает
Ты же понимаешь, что это вообще не аргумент в разговоре о языковых средствах, которые позволяют уменьшить вероятность ошибки.