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

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

4 минуты назад, haker_fox сказал:

virtual

Уже будет VTABLE, вызов через таблицу, а это накладные.

Почему нельзя наследовать от базового класса, в котором нет виртуальных методов?

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

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


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

23 minutes ago, Arlleex said:

Почему нельзя наследовать от базового класса, в котором нет виртуальных методов?

В этом случае будет перегрузка методов и, насколько я понимаю, теряется механизм полиморфизма. А вот дальше я что-то сам не могу разобраться, что это даёт... пойду stack overflow читать)

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


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

24 minutes ago, Arlleex said:

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

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

2 minutes ago, haker_fox said:

В этом случае будет перегрузка методов и, насколько я понимаю, теряется механизм полиморфизма

А ещё компилируется код, в котором не реализован метод write, ведь базовый класс становится реализованным (concrete) и может быть инстанциирован.

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

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


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

54 minutes ago, Forger said:

И где они теперь? )) Небось работают в серьезных индусских организациях

Вы переоцениваете отечественную школу программирования. У нас очень много любителей как создания godlike-объектов, так и нелюбителей абстракций, которые для перемещения лошади двигают не то, что её ногами, а атомами, из которых эта лошадь состоит.

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


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

44 minutes ago, Arlleex said:

Почему нельзя наследовать от базового класса, в котором нет виртуальных методов?

А вот, нашёл)

Если Вы перегружаете методы базового класса и методы в базовом классе у Вас объявлены без virtual, то может возникнуть такая ситуация. Вы объявляете указатель на базовый класс, и инициалиизируете этот указатель потомком, например обходя массив драйверов-потомков, вызывая метод init(). Если Вы вызовете теперь по указателю некий метод, то сработает метод из базового класса. А это может быть крайне нежелательно, ведь ждёте Вы, скорее всего, совсем другого поведения (что сработает метод класса потомка).

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


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

1 час назад, Arlleex сказал:

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

У меня это обычный класс. В нем содержатся функции работы с железом, которые абсолютно одинаковы для всех УАПП в кристалле и в нем же содержится указатель на базовый адрес регистров УАПП. Полиморфизм на этом уровне еще совершенно не нужен.

Напомню, речь в сообщении, на которое я отвечал, шла о построении некоего УАПП с очень уникальными свойствами, вряд ли на него сверху будет навешана косоль общения с пользователем - вероятнее всего он строится под какой-то конкретный протокол и только под него, а значит полиморфизм ему не нужен и не нужно пытаться подстроить его API под какой-то общий драйвер, можно прсто избежать дублирования кода каких-то общих для всех железных УАПП операций, поместив их в общий базовый класс. Точно так же бессмысленно унифицировать API у УАПП для modbus и УАПП для работы с консолью пользователя.

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


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

3 часа назад, Сергей Борщ сказал:

Он построен на базе железного УАПП контроллера?

да.

3 часа назад, Сергей Борщ сказал:

Если да - можно отнаследоваться от базового класса, имеющего примитивы работы с железным УАПП - настраивать скорость, четность и т.п и строить свою ветку классов от этого места, пусть даже не все функции базового класса будут использоваться.

А какой тогда смысл? Если только одна функция будет похожая на другие подобные - инициализация, но и та внутри (и по аргументам) - вся другая. Какой тогда смысл в таком наследовании? Когда собственно и ничего не наследуется от родительского.

Класс - он для объединения объектов, имеющих похожие свойства. Чем менее объекты имеют похожих свойств, тем такое объединение бессмысленнее и неэффективнее. Это просто уже - притягивание за уши классов туда, где они совсем не нужны. Только потому что модно/молодёжно.

Считаю что объединять в какие-то логические единицы (в виде класса или ещё как) нужно на более высоком уровне. Например (как у меня) - драйвер отладочного потока. Он передаёт поток как правило через UART (иногда бывают другие каналы) и внутри может вызывать функции установки скорости UART, передачи/приёма символов и т.п. Которые - обычные функции. Т.е. - построение по типу: middleware / IO-уровень.

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


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

2 часа назад, haker_fox сказал:

Позволю себе ответить, хоть вопрос и не мне. В таком классе могут быть как чисто виртуальные методы, конкретное содержимое которых прописывается в классе, реализующем код. Например, метод write невозможно заранее определить для всех классов драйверов, т.к. аппаратная составляющая в каждом случае уникальна.

А какой тогда смысл в таком классе? Если следовать вашей логике, то тогда надо создать такой базовый класс для всех устройств ввода/вывода - UART, USB, SPI и т.п. А какой смысл в таком объединении и чем оно лучше простого UartWrite(), SpiWrite() если кроме имён методов оно ничего не объединяет. Это уже искусственное притягивание классов за уши туда, где они совсем не нужны. И бесполезное загромождение кода бессмысленными сущностями.

 

Цитата

И тогда каждый класс драйвера, отнаследованный от базового, обязан предоставить этот интерфейс.

Так "обязан" или "может"? По Вашим же постам: Если у вас в одном проекте нужна функция Close() для UART, а вдругом - не нужна, то даже там где она не нужна, всё равно нужно её реализовывать? Или нет?

Цитата

С другой стороны, настройки того же последовательного порта в обще случае платформенно-независимы (скорость, количество бит данных, чётность, количество стоповых бит)

Это с чего они "платформонезависимы"??? Вы ведь мой пример выше читали, забыли? Там где размер символа UART = 24 бита. Сможете Вы на STM32 или на LPC такую длину символа установить?

 

Цитата

А именно: сама структура, описывающая настройки; и методы, записывающие эти настройки, проверяющие их корректность, возвращающие их по требованию и т.п.

Вот опять оверхед! О котором я говорил. Вы похоже не замечаете просто этого.... Нафига мне создавать и передавать какую-то структуру с настройками в функцию инита порта, если у меня в данном проекте нужно для UART устанавливать только скорость (остальные все параметры = const). Только потому, что в каком-то N-ном проекте мне в UART требовалась не только скорость, но и ещё куча параметров? И потому теперь, во всех проектах вместо передачи бодового значения скорости по значению, мне нужно везде тащить этот интерфейс со структурой!  :shok:

1 час назад, Arlleex сказал:

Уже будет VTABLE, вызов через таблицу, а это накладные.

Вот именно! А классо-поклонники я вижу просто не замечают этого. :biggrin:

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


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

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

А какой тогда смысл? Если только одна функция будет похожая на другие подобные - инициализация

Избежать дублирования исходного кода. Если у вас там вообще ничего общего - то и смысла нет, разумеется.

У меня же скорость может настраиваться отдельно от инициализации и, соответственно, есть одна для всех УАПП функция настройки скорости в классе variable_speed_uart, которому в качестве базового через параметр шаблона может быть указан generic_uart (который хранит указатель на базовый адрес УАПП и скрывает разницу в именах флагов разных моделей STM32). Аналогично потомкам базовый класс передается в качестве параметра шаблона и можно собирать любую нужную мне комбинацию, например

using serial_port = shutdownable_uart<rx_irq<tx_dma<variable_speed_uart<generic_uart>, 8192>, 8>>

 

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


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

1 час назад, haker_fox сказал:

В этом случае будет перегрузка методов и, насколько я понимаю, теряется механизм полиморфизма. А вот дальше я что-то сам не могу разобраться, что это даёт... пойду stack overflow читать)

Вы на ровном месте сделали сущестенный оверхед. Совершенно искусственно. Даже не заметив этого.

Это собственно то, с чего я и написал Вам изначальное замечание. С которым Вы всё продолжаете спорить, говоря что "не спорите". И каждый раз, своими же примерами, доказываете мою правоту!  :biggrin: 

 

PS: Да и не будет там перегрузки. Метод же виртуальный. Т.е. - указатель. Будет то, что сказал Arlleex.

 

1 час назад, one_eight_seven сказал:

У нас очень много любителей как создания godlike-объектов, так и нелюбителей абстракций, которые для перемещения лошади двигают не то, что её ногами, а атомами, из которых эта лошадь состоит.

Я смотрю - тут как раз много любителей создания лошади из каждого атома.  :biggrin:

1 час назад, haker_fox сказал:

Если Вы перегружаете методы базового класса и методы в базовом классе у Вас объявлены без virtual

Жесть какая! Теперь ещё для инита порта, вместо простого BL, будет стоять кучка команд LDR с последующей BLX: чтение указателя на таблицу виртуальных методов, чтение указателя на конкретный виртуальный метод (Init()), а затем - BLX. И вся эта кухня - вместо простого BL! (которого может и не быть если компилятор заинлайнит его).

Ну ладно ещё инит порта, но ведь и чтение/запись символов будет работать через такой-же бурелом!!! Каждый символ!  :shok:

А потом удивляемся, почему на казалось бы ещё вчера мощном CPU, сегодня наша программа еле ворочается.  :biggrin:

Вот это и есть - оверхед в полный рост!

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


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

34 minutes ago, jcxz said:

Класс - он для объединения объектов, имеющих похожие свойства.

Есть и другой подход - использовать его для описания объектов, имеющих похожее поведение, а свойств он может вообще не иметь.

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


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

1 час назад, Сергей Борщ сказал:

вероятнее всего он строится под какой-то конкретный протокол и только под него

Конечно. И предназначен для передачи не только собственно кадра данных (единый 24-битный кадр (точнее = 26-битный, если считать со старт- и стоп-битами)), но и точки точной синхронизации (по началу старт-бита) между двумя устройствами.

 

Цитата

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

Вот именно об этом я и пишу!

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


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

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

Spoiler

just-because-you-are-unique-doesnt-mean-

 

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


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

17 минут назад, one_eight_seven сказал:

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

  Скрыть содержимое

just-because-you-are-unique-doesnt-mean-

 

Вспоминается тут мне один мой бывший коллега. Который несколько лет назад решил освоить ООП. И начал везде где ни попадя создавать классы. Со всякими наследованиями и перегрузками. Я потом заглянул в его код и......! о УЖАС!!! :shok:   Там где раньше были вызовы типа: SetEvent(...), ResetEvent(), SetAlarm(...), ResetAlarm(...), SetState(), ResetState() и т.п. с говорящими названиями, когда глядя на такой код более/менее понимаешь, что там происходит. То теперь там значилось:

некий_объектA_с_ничего_не_значащим_названием.Set(...);

некий_объектB_с_ничего_не_значащим_названием.Set(...);

некий_объектC_с_ничего_не_значащим_названием.Reset(...);

... и так несколько десятков set()/reset()/get() в одной функции, но для разных переменных разного типа.  :dash2:

вобщем - код состоящий из огромного множества Set()/Reset()/Get() и т.п. методов с одинаковыми названиями, но внутри совершенно разных. А чтобы понять - что за Set() в данном месте, нужно каждый раз отматывать исходник к месту объявления каждого такого объекта, чтобы увидеть его тип.

Таким образом - вместо улучшения читаемости (которое вроде должно давать применение ООП), получили прямо противоположный результат. Стало совершенно нечитаемо.

Если раньше можно было просто поиском по имени функции, найти место её определения и посмотреть что она делает, то теперь поиск выдавал тучу одинаковых Set(). :dash2:

Всё как на вашей каринке - вместо столового прибора на столе: с ложкой, вилкой, ножом, тарелкой, получили вот такой ряд из одних вилок!  :dash2:

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


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

56 минут назад, jcxz сказал:

Вот именно об этом я и пишу!

Нет, вы пишете об обратном:

21 час назад, jcxz сказал:

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

А "такое" засовывать не нужно. Для этого программисту голова и дана.

Опишу свое решение, может оно натолкнет кого-то на полезную мысль. У меня дерево классов работы с УАПП не имеет виртуальных функций. А вот класс console имеет виртуальные функции write(), read(), is_connected() и уже от console наследуются serial_console, vcp_console и telnet_console, членами которых являются, соответственно, ссылки на serial_port, usb_device::vcp_interface и telnet. И еще один класс, терминал, соотвественно, имеет указатель на console. В результате в устройстве может быть несколько независимых одновременно работающих терминалов на разных интерфейсах.

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


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

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

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

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

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

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

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

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

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

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