#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.
Здравствуйте, Marty, Вы писали:
M>Вот и я про что. Ну, grep static_cast, и что? Оно выдаст килотонны этих кастов, и накуя это нужно? А каст к конкретному типу можно и в случае сишного каста найти
Если миллионы, то следующая напрашивающаяся команда — rm -rf
Ну я если их немного, так можно их всех оптом поискать и посмотреть, так ли уж они нужны.
Но в общем и целом, если ты мое личное мнение спросишь, я не вижу в сишном касте большого греха. Особенно если учесть, что мой рабочий язык — Си, не C++
Здравствуйте, 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);
}
Здравствуйте, so5team, Вы писали:
S>>>Вот, из реального проекта (комментарии изъяты): BFE>>жуть.
S>Нам очень важно ваше мнение, не сдерживайтесь. Оно имеет особую ценность в свете вот этого комментария: http://rsdn.org/forum/cpp/8570137.1
Вообще да, лично я согласен с B0FEE664, код максимально ужасен. Возможно, он всё формальные проверки и проходит, но он написан не для человека, который будет его поддерживать
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).
Хотя я бы подпёр юнит-тестом просто чтобы показать коллегам, что результат проверен.
Здравствуйте, so5team, Вы писали: S>Чтобы сделать разговор более предметным, три вопроса к вам: S>1. У вас такой же список претензий/замечаний/вопросов/домыслов, как и у B0FEE664? Или же есть что добавить?
Я не вчитывался в смысл, потому что от одного оформления у меня кровь из глаз пошла
S>2. Если, по вашему мнению, код "максимально ужасен", то что могло бы его "исправить" (опять же по вашему мнению)?
Для начала — нормально оформить
S>3. Могли бы вы, для симметрии, привести фрагмент своего кода с кастами, дабы проиллюстрировать как должен выглядеть нормально написанный вами код?
DP>Еще одна мысль: сишные касты — это как топор. Ты им можешь что угодно отрубить, покромсать, оттяпать и так далее. А плюсовые касты — это набор разных специализированных инструментов, каждый под свою задачу. Используя касты ты даешь понять читателю, что ты хотел сделать этой конструкцией. Например, что вот тут ты просто убираешь const, а вот тут преобразуешь к наследуемому типу. Это делает код более читаемым и ясным -> снижает вероятность ошибки.
Это делает код более захламлённым => менее читаемым и ясным.
Представь себе, что код ещё и какую-то логику делает, а не только типы преобразовывает.
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>Мне приходилось видеть и использовать разные стили оформления, так что на внешний вид, по большей части, пофиг, если только аффтары не злоупотребляют строками по 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>Да, не трудился. На автомате
О том и речь. Обилие кастов в коде -- это явный знак, что где-то что-то недодумано.