Перейти к содержанию
    

Плавный переход C -> C++ под МК

13 minutes ago, Arlleex said:

Но разве этот switch-case нагляден?

Да, если весь case в одну строку, то очень наглядно ))

Но повторюсь, это скрыто внутри драйвера, отлажено и забыто. Больше туда лазить смысла нет.

 

Quote

А если там этих возможных вариантов будет сотня-тысяча?

Для таких случаев применю просто тип данных без всяких перечислений и контроль границ с соотв ассертом. Например, драйвер для SerialPort (USART)

Для CAN я описал конкретный случай, который не требует сложных конструкций. Нет смысла обобщать простые конструкции - значения скоростей для CAN можно сказать стандартизированы, их немного совсем. Поэтому switch-case вполне уместен.

 

Quote

На самом деле можно приводить миллион примеров, когда без явного cast-а лаконично не обойтись.

Примеры.

1. Функция чтения занятого места в программной очереди (FIFO)

Если очередь построена на базе шаблона, не привязана к конкретным типам данных, то все делается несколько проще :) Опять-таки тоже без cast.

 

Quote

2. Запись адресов каких-то объектов в регистры периферии

Да, с регистрами периферии и другой низкоуровневой "ботвой" по-другому действительно никак :(

Но это тоже все можно скрыть внутри неких драйверов, отладить и забыть. Глаз мозолить не будут )

 

Quote


3. Использование функций с несовместимыми указателями

А тут уже лучше работать со ссылками, а не указателями )

Я редко использую указатели как таковые. А, если применяю, то исключительно локально внутри функций или внутри драйверов.

Цель - максимально ограничить их область видимости, т.к. указатель по мне - потенциально ОПАСНЫЙ объект.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, Forger сказал:

Если очередь построена на базе шаблона, не привязана к конкретным типам данных, то все делается несколько проще. Опять-таки тоже без cast.

А это как? Позиции чтения/записи все равно придется описывать вполне конкретными типами данных.

Цитата

А тут уже лучше работать со ссылками, а не указателями )

Так ведь в очередь я могу передавать вообще что угодно. "Обобщенную" ссылку (void &) ведь сделать нельзя. И в чем "опасность" указателя? А если мне надо записать в очередь целую кучу каких-то данных, размер которых заранее не известен? Не совсем понятна "безопасность" ссылки в этом отношении.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Принципиальных отличий два:

  • ссылка, в отличии от указателя, не может быть неинициализированной;
  • ссылка не может быть изменена после инициализации.

Отсюда и получаем плюсы и минусы использования того и другого:

  • ссылки лучше использовать когда нежелательно или не планируется изменение связи ссылка → объект;
  • указатель лучше использовать, когда возможны следующие моменты в течении жизни ссылки:
    • ссылка не указывает ни на какой объект;
    • ссылка указаывает на разные объекты в течении своего времени жизни.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Про различие static_cast и reinterpret_cast сказали выше - это очень разные "по силе" касты (и последствия от преобразований тоже разные), но С не различает их в своём синтаксисе. А смысл их такого громоздкого синтаксиса именно в том, чтобы они  своим безобразным видом прямо-таки подсвечивали место потенциальной ошибки (там где компилятору велели "умыть руки" и не контролировать работы с типами). Каст на С в коде зачастую нетрудно пропустить, т.к. это обычное выражение с круглыми скобками, коими С/C++ злоупотребляют. А *_cast<...>() кроме того ещё и подсвечивается в программерских редакторах, т.к. являются ключевыми словоми языка.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, Forger сказал:

Принципиальных отличий два:

  • ссылка, в отличии от указателя, не может быть неинициализированной;
  • ссылка не может быть изменена после инициализации.

Этими же свойствами обладает константный указатель (не путать с указателем на константу). Например:

char a;
char * const ptr_a = &a; // создание и инициализация константного указателя на char

Здесь указатель ptr_a нельзя оставить неинициализированным при его создании. Также, ему нельзя присвоить адрес иного объекта программы (нельзя изменить) т.к. он константный, т.е. неизменяемый.

Вот тут приведён более широкий список различий ссылок и указателей.

Изменено пользователем Darth Vader

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кстати да! Ссылка же должна указывать на объект в памяти (т.е. она не может быть 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.

Ссылка здесь не поможет. В общем, есть достоинства и недостатки и у указателей и у ссылок.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 minutes ago, Arlleex said:

Т.е. вызвав read(NULL, 10) мы просто говорим "прочитать 10 элементов впустую". 

А какой в этом смысл? Это похоже на некий костыль или я чего-то не понимаю.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, Forger сказал:

А какой в этом смысл? Это похоже на некий костыль или я чего-то не понимаю.

Бывает нужно просто скипнуть какое-то количество данных в FIFO, так как они по какой-то причине оказались не нужны для обработки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, Arlleex said:

Бывает нужно просто скипнуть какое-то количество данных в FIFO, так как они по какой-то причине оказались не нужны для обработки.

Для такого необычного случая я бы сделал отдельный метод для класса очереди, который делает именно это. В коде это выглядело бы очень наглядно именно по названию метода:

s32 cFIFO::skip(u32 len) 

В вашем же случае в коде придется добавлять комментарий, иначе такое написание вводит в заблуждение.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 минут назад, Forger сказал:

Для такого необычного случая я бы сделал отдельный метод для класса очереди, который делает именно это.

Дело в том, что код read() и такой функции skip() будет на 99% одинаковый, и к сожалению, если в будущем захочется что-то исправить в одной (например, по причине бага), то нужно будет не забыть исправить этот код и в другой. Я стараюсь избегать такого подхода с дублированием кода (естественно, если накладные расходы крайне незначительны, а один лишний if() в read() как раз этот случай). Я думаю, в данном случае можно попробовать создать inline функцию в классе, которая будет вызывать внутри себя read(NULL, len). Ну либо другой способ: dst в функции read() задать NULL по умолчанию. Тогда вызов read(10) как бы намекает, что нужно прочитать 10 элементов в никуда.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 minutes ago, Arlleex said:

Дело в том, что код read() и такой функции skip() будет на 99% одинаковый, и к сожалению, если в будущем захочется что-то исправить в одной (например, по причине бага), то нужно будет не забыть исправить этот код и в другой. 

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

Его и вызывайте из public методов, чуть ли прямо не описании класса, чтобы не тащить реализацию в cpp файл. Очень наглядно и решает вашу "проблему", нет никакого дублирования кода. Я бы в итоге именно так сам  и сделал )

 

11 minutes ago, Arlleex said:

Тогда вызов read(10) как бы намекает, что нужно прочитать 10 элементов в никуда.

Это очень плохая практика - давать намеки! 

Я все же - сторонник конкретики, по крайней мере в коде :) Поэтому сделал бы просто метод skip, который внутри вызывал бы ваш "толстый", но уже private метод.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 часов назад, dxp сказал:

А смысл их такого громоздкого синтаксиса именно в том, чтобы они  своим безобразным видом...

Угу, вот была же аккуратная функция с небольшим размахом строк... И тут на тебе:crazy::biggrin:

Скрытый текст

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;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И сразу отлично видно потенциально проблемное место, на которое компилятор промолчит, и сразу видно, что к чему кастуется. 

 

Вот это:

if(br >= 0 && !can->setBitRate((cCAN::eBitRate)(br + 1)))

выглядит ничем не лучше, даже наоборот - нужно прилагать усилия, чтобы понять, что тут ручное преобразование типов, а не просто скобки для выделения приоритетов операций. И вдобавок тут может скрываться злобный reinterpret_cast, если, скажем, тип, к которому делается преобразование, например, указатель. static_cast это с ходу отловит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую всех.

Итак, вопрос следующий: а какой практический смысл в const-методах класса? Да, мы говорим компилятору, что мы этому методу передаем неявно class_type *const this. Т.е. при поытках изменить данные класса вылезет ошибка. Но тут же мы идем в описание этого класса и удаляем const у метода. Я еще понимаю, когда все методы класса - константные (хотя это сложно представить). Но когда класс - сборная солянка из const- и не-const-методов - какой смысл запрещать изменять данные всем этим const-методам?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И все-таки. Есть у меня незакрытый гештальт, который я уже пару месяцев назад пытался решить. Назначенная инициализация в 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-и:mda:Я уже готов тупо подавить конкретно этот warning, только вот как, чтобы еще не задавить остальные причастные warning-и?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...