Forger 0 Posted February 22 · Report post 13 minutes ago, Arlleex said: Но разве этот switch-case нагляден? Да, если весь case в одну строку, то очень наглядно )) Но повторюсь, это скрыто внутри драйвера, отлажено и забыто. Больше туда лазить смысла нет. Quote А если там этих возможных вариантов будет сотня-тысяча? Для таких случаев применю просто тип данных без всяких перечислений и контроль границ с соотв ассертом. Например, драйвер для SerialPort (USART) Для CAN я описал конкретный случай, который не требует сложных конструкций. Нет смысла обобщать простые конструкции - значения скоростей для CAN можно сказать стандартизированы, их немного совсем. Поэтому switch-case вполне уместен. Quote На самом деле можно приводить миллион примеров, когда без явного cast-а лаконично не обойтись. Примеры. 1. Функция чтения занятого места в программной очереди (FIFO) Если очередь построена на базе шаблона, не привязана к конкретным типам данных, то все делается несколько проще :) Опять-таки тоже без cast. Quote 2. Запись адресов каких-то объектов в регистры периферии Да, с регистрами периферии и другой низкоуровневой "ботвой" по-другому действительно никак :( Но это тоже все можно скрыть внутри неких драйверов, отладить и забыть. Глаз мозолить не будут ) Quote 3. Использование функций с несовместимыми указателями А тут уже лучше работать со ссылками, а не указателями ) Я редко использую указатели как таковые. А, если применяю, то исключительно локально внутри функций или внутри драйверов. Цель - максимально ограничить их область видимости, т.к. указатель по мне - потенциально ОПАСНЫЙ объект. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted February 22 · Report post 3 минуты назад, Forger сказал: Если очередь построена на базе шаблона, не привязана к конкретным типам данных, то все делается несколько проще. Опять-таки тоже без cast. А это как? Позиции чтения/записи все равно придется описывать вполне конкретными типами данных. Цитата А тут уже лучше работать со ссылками, а не указателями ) Так ведь в очередь я могу передавать вообще что угодно. "Обобщенную" ссылку (void &) ведь сделать нельзя. И в чем "опасность" указателя? А если мне надо записать в очередь целую кучу каких-то данных, размер которых заранее не известен? Не совсем понятна "безопасность" ссылки в этом отношении. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Forger 0 Posted February 22 · Report post 16 minutes ago, Arlleex said: А это как? Позиции чтения/записи все равно придется описывать вполне конкретными типами данных. Наверно мы о разных очередях говорим )) Я имел ввиду вот такую очередь: template <class Item, uint32_t SIZE> class Queue { public: Queue() : head(0), tail(0), freeSize(SIZE) { } bool isPutDone(const Item & item) { ... } bool isGetDone(Item & item) { .... } void tryToGet(Item & item) { isGetDone(item); } ... Quote Так ведь в очередь я могу передавать вообще что угодно. "Обобщенную" ссылку (void &) ведь сделать нельзя. Нет, конечно! Такая самодеятельно строго запрещена. У меня очередь может быть только очередью объектов ОДНОГО типа, разных - нет. Разные уже можно упаковать внутрь самого объекта, добавив ему соотв. методы. Quote И в чем "опасность" указателя? В его непредсказуемости когда проект развивается. Я не раз наступал на грабли с указателями и потому их избегаю насколько это возможно. Quote А если мне надо записать в очередь целую кучу каких-то данных, размер которых заранее не известен? Для этого следует использовать другие конструкции. Например, разношерстность данных упаковать внутрь некого класса-контейнера, а уже в очередь добавлять экземпляры этих классов-контейнеров. Так сказать изолировать мух от котлет )) Quote Не совсем понятна "безопасность" ссылки в этом отношении. Ну, про "ссылки vs указатели" наверно тут уже нет смысла писать, это довольно популярно уже расписано в сторонней литературе )) Вот небольшая цитата со стековерфлоу: Quote Принципиальных отличий два: ссылка, в отличии от указателя, не может быть неинициализированной; ссылка не может быть изменена после инициализации. Отсюда и получаем плюсы и минусы использования того и другого: ссылки лучше использовать когда нежелательно или не планируется изменение связи ссылка → объект; указатель лучше использовать, когда возможны следующие моменты в течении жизни ссылки: ссылка не указывает ни на какой объект; ссылка указаывает на разные объекты в течении своего времени жизни. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
dxp 0 Posted February 23 · Report post Про различие static_cast и reinterpret_cast сказали выше - это очень разные "по силе" касты (и последствия от преобразований тоже разные), но С не различает их в своём синтаксисе. А смысл их такого громоздкого синтаксиса именно в том, чтобы они своим безобразным видом прямо-таки подсвечивали место потенциальной ошибки (там где компилятору велели "умыть руки" и не контролировать работы с типами). Каст на С в коде зачастую нетрудно пропустить, т.к. это обычное выражение с круглыми скобками, коими С/C++ злоупотребляют. А *_cast<...>() кроме того ещё и подсвечивается в программерских редакторах, т.к. являются ключевыми словоми языка. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Darth Vader 0 Posted February 23 (edited) · Report post 10 часов назад, Forger сказал: Принципиальных отличий два: ссылка, в отличии от указателя, не может быть неинициализированной; ссылка не может быть изменена после инициализации. Этими же свойствами обладает константный указатель (не путать с указателем на константу). Например: char a; char * const ptr_a = &a; // создание и инициализация константного указателя на char Здесь указатель ptr_a нельзя оставить неинициализированным при его создании. Также, ему нельзя присвоить адрес иного объекта программы (нельзя изменить) т.к. он константный, т.е. неизменяемый. Вот тут приведён более широкий список различий ссылок и указателей. Edited February 23 by Darth Vader Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted February 23 · Report post Кстати да! Ссылка же должна указывать на объект в памяти (т.е. она не может быть NULL). У меня, например, есть функции, которые могут принимать NULL как вполне допустимый операнд s32 cFIFO::read(u8 dst[], u32 len) { // вычисление занятого места // и следующей позиции nxtRPos ... if(dst != NULL) { // чтение из FIFO в dst[] } rpos = nxtRPos; ... } Т.е. вызвав read(NULL, 10) мы просто говорим "прочитать 10 элементов вхолостую". А вот вызвав read(buf, 10) мы просим реально считать 10 элементов из FIFO в buf. Ссылка здесь не поможет. В общем, есть достоинства и недостатки и у указателей и у ссылок. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Forger 0 Posted February 23 · Report post 5 minutes ago, Arlleex said: Т.е. вызвав read(NULL, 10) мы просто говорим "прочитать 10 элементов впустую". А какой в этом смысл? Это похоже на некий костыль или я чего-то не понимаю. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted February 23 · Report post 1 минуту назад, Forger сказал: А какой в этом смысл? Это похоже на некий костыль или я чего-то не понимаю. Бывает нужно просто скипнуть какое-то количество данных в FIFO, так как они по какой-то причине оказались не нужны для обработки. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Forger 0 Posted February 23 · Report post 2 minutes ago, Arlleex said: Бывает нужно просто скипнуть какое-то количество данных в FIFO, так как они по какой-то причине оказались не нужны для обработки. Для такого необычного случая я бы сделал отдельный метод для класса очереди, который делает именно это. В коде это выглядело бы очень наглядно именно по названию метода: s32 cFIFO::skip(u32 len) В вашем же случае в коде придется добавлять комментарий, иначе такое написание вводит в заблуждение. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted February 23 · Report post 5 минут назад, Forger сказал: Для такого необычного случая я бы сделал отдельный метод для класса очереди, который делает именно это. Дело в том, что код read() и такой функции skip() будет на 99% одинаковый, и к сожалению, если в будущем захочется что-то исправить в одной (например, по причине бага), то нужно будет не забыть исправить этот код и в другой. Я стараюсь избегать такого подхода с дублированием кода (естественно, если накладные расходы крайне незначительны, а один лишний if() в read() как раз этот случай). Я думаю, в данном случае можно попробовать создать inline функцию в классе, которая будет вызывать внутри себя read(NULL, len). Ну либо другой способ: dst в функции read() задать NULL по умолчанию. Тогда вызов read(10) как бы намекает, что нужно прочитать 10 элементов в никуда. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Forger 0 Posted February 23 · Report post 11 minutes ago, Arlleex said: Дело в том, что код read() и такой функции skip() будет на 99% одинаковый, и к сожалению, если в будущем захочется что-то исправить в одной (например, по причине бага), то нужно будет не забыть исправить этот код и в другой. Это очень просто реализуется - сделайте private метод, который сделан так как вам удобно, с кучей аргументов, если нужно. Его и вызывайте из public методов, чуть ли прямо не описании класса, чтобы не тащить реализацию в cpp файл. Очень наглядно и решает вашу "проблему", нет никакого дублирования кода. Я бы в итоге именно так сам и сделал ) 11 minutes ago, Arlleex said: Тогда вызов read(10) как бы намекает, что нужно прочитать 10 элементов в никуда. Это очень плохая практика - давать намеки! Я все же - сторонник конкретики, по крайней мере в коде :) Поэтому сделал бы просто метод skip, который внутри вызывал бы ваш "толстый", но уже private метод. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted February 23 · Report post 6 часов назад, dxp сказал: А смысл их такого громоздкого синтаксиса именно в том, чтобы они своим безобразным видом... Угу, вот была же аккуратная функция с небольшим размахом строк... И тут на тебе Скрытый текст cSLCAN::eFlow cSLCAN::parseClosed(u8 buf[], u32 &len) const { u32 plen = 0; eFlow flow = eFlow::INCOMPLETE; if(len >= 2) { u8 ack = ERR; const u8 ch1 = buf[0], ch2 = buf[1]; if(ch1 == 'O') { if(ch2 == CR) { if(!can->open()) ack = OK; flow = eFlow::SYNC; } else flow = eFlow::UNSYNC; stx->write(&ack, 1); plen = 2; } else if(len >= 3) { const u8 ch3 = buf[2]; flow = eFlow::UNSYNC; plen = 1; if(ch1 == 'S') { if(ch3 == CR) { s32 br = hexToInt1(ch2); if(br >= 0 && !can->setBitRate(static_cast<cCAN::eBitRate>(br + 1))) ack = OK; flow = eFlow::SYNC; plen = 3; } } stx->write(&ack, 1); } } len = plen; return flow; } Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
dxp 0 Posted February 24 · Report post И сразу отлично видно потенциально проблемное место, на которое компилятор промолчит, и сразу видно, что к чему кастуется. Вот это: if(br >= 0 && !can->setBitRate((cCAN::eBitRate)(br + 1))) выглядит ничем не лучше, даже наоборот - нужно прилагать усилия, чтобы понять, что тут ручное преобразование типов, а не просто скобки для выделения приоритетов операций. И вдобавок тут может скрываться злобный reinterpret_cast, если, скажем, тип, к которому делается преобразование, например, указатель. static_cast это с ходу отловит. Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted March 5 · Report post Приветствую всех. Итак, вопрос следующий: а какой практический смысл в const-методах класса? Да, мы говорим компилятору, что мы этому методу передаем неявно class_type *const this. Т.е. при поытках изменить данные класса вылезет ошибка. Но тут же мы идем в описание этого класса и удаляем const у метода. Я еще понимаю, когда все методы класса - константные (хотя это сложно представить). Но когда класс - сборная солянка из const- и не-const-методов - какой смысл запрещать изменять данные всем этим const-методам? Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...
Arlleex 1 Posted March 5 · Report post И все-таки. Есть у меня незакрытый гештальт, который я уже пару месяцев назад пытался решить. Назначенная инициализация в C++. У меня в Keil-е C++11. Соответственно, при компиляции следующего кода вылазит предупреждение struct sHwCfg { struct sGPIO { u32 pclkEnPos; GPIO_TypeDef &hw; u8 pinNum : 4; }gpio[2]; struct sCAN { u32 pclkEnPos; CAN_TypeDef &hw; }can; }static const HwCfgTbl[] = { #define INIT(n, rxgpio, rxpin, txgpio, txpin, can) \ [static_cast<u32>(cCAN::eHwCfg::n)] = { \ {RCC_AHB1ENR_GPIO##rxgpio##EN, *GPIO##rxgpio, rxpin, \ RCC_AHB1ENR_GPIO##txgpio##EN, *GPIO##txgpio, txpin}, \ {RCC_APB1ENR_CAN##can##EN, *CAN##can}} INIT(CAN1_PA11_PA12, A, 11, A, 12, 1), INIT(CAN2_PB12_PB13, B, 12, B, 13, 2) #undef INIT }; sources/drivers/can.cpp(49): warning: array designators are a C99 extension [-Wc99-designator] Понятно, что компилятор не хочет, чтобы я "вручную" указывал элементы массива в списке инициализации. Но мне так удобнее читать код. CAN1_PA11_PA12, CAN2_PB12_PB13 - это enum-ы. Естественно, в этот enum могут добавляться новые значения, причем в любом месте. Соответственно, инициализируемый массив не должен от этого "рассинхронизироваться" с назначаемыми элементами инициализации. Тем более у меня массив этих структур - константный: положения всех элементов известны на этапе компиляции программы - ан-нет, все равно держи warning-иЯ уже готов тупо подавить конкретно этот warning, только вот как, чтобы еще не задавить остальные причастные warning-и? Quote Ответить с цитированием Share this post Link to post Share on other sites More sharing options...