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

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

11 часов назад, std сказал:

В истории языка не было каких-либо "отмен".

new(std::size_t, std::size_t)

Цитата

The placement form void* operator new(std::size_t, std::size_t) is not allowed because the matching signature of the deallocation function, void operator delete(void*, std::size_t), is a usual (not placement) deallocation function.  (since C++14)

 

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


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

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

Уже затертые до дыр примеры из учебников или ютуба про наследование собак и котов от абстрактного Animals ограничиваются только тем, что в цикле можно пройтись по массиву указателей на этот абстрактный Animals (при этом реальные указатели, соответственно, на объектах собак и котов) и вызвать у них метод Voice(). И все! Да я и в чистых Си могу так сделать - в структуре Animals поместить указатель на функцию Voice(), а структуры собак или котов будут или содеражть этот Animals в строго определенном месте, либо держать этот указатель на Voice() ровно в тех же смещениях, как и в структуре Animals. И ровно так же в массиве указателей то на собак, то на котов, но обобщенных указателем на Animals, гавкать и мяукать. И что же, это ООП-подход? А по-моему просто синтаксический сахар - как видно в Си это монструозно, а в плюсах нативно.

 

В чистом С вам придётся в каждой структуре хранить указатель (vptr) на массив указателей (vtbl), создавать для каждого типа структуры этот массив, правильно инициализировать его. Адресом этого массива нужно проинициализровать vptr в каждой структуре, не перепутав где чей массив указателей. Вызов этот будет выглядеть как цепочка разыменовываний с вызовом в конце. И т.д.

Тут немалое поле для ошибок. Вместо профита это будет выглядеть как микрофилиал ада. В С++ это ООП сводится к определениям и вызов - одна строчка. Одинаковая вне зависимости от конкретного типа и объекта. В этом суть полиморфизма - вид один и тот же, а поведение разное. И компилятор тут не ошибается, делает всю эту путанную рутину на раз.

Кроме того, наследование даёт возможность нативно и прозрачно расширять функциональность. На С вам придётся либо переписывать руками код, либо включать в структуру структуру-"предка" в качестве члена, а это порождает для неё дополнительный уровень иерархии там, где он совсем не нужен.

Ну, а так и С тогда "синтаксический сахар" для ассемблера. 🙂

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


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

37 minutes ago, dxp said:

Ну, а так и С тогда "синтаксический сахар" для ассемблера. 🙂

Не удержусь и продолжу ассоциативную цепочку: ассемблер - "синтаксический сахар" для машинных кодов. Или, что более верно, "сахарная пудра", т.к. полного засахаривания всё же не будет.

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


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

48 minutes ago, Arlleex said:

Вот пример: мне нужно принять строку по UART и выдать ее на дисплей, подключенный по SPI к тому же микроконтроллеру.

 

50 minutes ago, Arlleex said:

А теперь: кто-то должен вызвать ReadString() и поместить считанную строку в Print() экрана. Кто?

Вот есть у меня в ПЛК логгер. Он использует алгоритм Нэйгла, который берёт строки из буфера и отправляет их на печать. Строки складывают в буфер несколько задач. Ессно, возникает вопрос, кто будет запускать отправку? Очень просто: если очередной символ невозможно поместить в буфер, то запускается отправка содержимого буфера для его опорожнения. Или же отправка запускается по таймеру, потому что логгер наследует метод таймера, вызываемый по прерыванию. Так что _никаких_ функционального взаимодействий между объектами нет.

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


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

6 минут назад, tonyk_av сказал:

Или же отправка запускается по таймеру, потому что логгер наследует метод таймера, вызываемый по прерыванию. Так что _никаких_ функционального взаимодействий между объектами нет.

Это уже костыль - таймеры и т.д. У меня тоже есть такой алгоритм. Я просто пишу в очередь write(), а внутри драйвера определяется, нужно ли в конце этого write() пнуть DMA, или он уже итак запущен и его пинать не нужно. DMA работает цепочечно - в прерывании по завершению транзакции смотрит в буфер - если там данные для отправки есть - вычисляет сколько их и заряжает отправку. И так по кругу. И это не означает, что никаких функциональных взаимодействий нет. Вы пишете, что никаких взаимодействий нет - но на самом деле толку от объекта, который живет сам по себе и никак не взаимодействует с внешним миром нет, и вы это итак подтвердили - этому вашему объекту надо каким-то образом "поместить символ в буфер". Значит некий кто-то пытается класть символ в буфер этого объекта - т.е. взаимодействие есть. То, что у объекта могут быть собственные механизмы внутри для поддержания некой функциональности - это совсем другое.

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


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

1 hour ago, Arlleex said:

Это уже костыль - таймеры

Это не костыль, а основа работы. Логгер может гнать данные не только в UART, но и в сетевой контроллер, оттуда и растут ноги алгоритма Нэйгла.

3 hours ago, Arlleex said:

И это будет самый банальный функциональный стиль вызова методов

Речь об этом, а не об отсутствии взаимодействия. Суть ООП как раз не в том, чтобы просто изолировать объекты, а в том, чтобы создать объекты, которые общаются друг с другом, не зная ничего о внутреннем устройстве друг друга. Тот же логгер не знает ничего о физическом интерфейсе, через который уходят данные, UART, USB или Ethernet.

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


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

24 минуты назад, tonyk_av сказал:

Речь об этом, а не об отсутствии взаимодействия. Суть ООП как раз не в том, чтобы просто изолировать объекты, а в том, чтобы создать объекты, которые общаются друг с другом, не зная ничего о внутреннем устройстве друг друга. Тот же логгер не знает ничего о физическом интерфейсе, через который уходят данные, UART, USB или Ethernet.

Так и нормальные Сишные программы изолируют реализации тех или иных физических интерфейсов, и некий send() может отправлять и в UART, и в USB, и в Ethernet.

И в Си разные модули оформлены в виде отдельных единиц трансляции, чтобы не путаться (да как и в C++ тоже). Наружу торчит .h с неким API, который можно унифицировать для разных интерфейсов.

И что, это ООП? Нет, это просто хорошо структурированная программа.

Пока что все доводы, якобы применительные только к ООП-парадигме, звучат как некая надстройка над банальным функциональным кодированием, с определенными соглашениями о структуризации программы, которые еще и нативно хорошо поддержаны в соответствующих ООП ЯП. Нет никакой ООП-магии, я вижу все тот же код, все те же вызовы методов разных объектов, все те же банальные конструкции функциональных ЯП в C++ коде. Да, за счет парадигмы более строгой структуризации программы нам легче ее читать, в отличие от большинства Си программ, которые разработчики структурируют кто во что горазд.

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


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

On 9/12/2023 at 2:08 PM, Arlleex said:

Нет никакой ООП-магии

мне вообще непонятно какой смысл менять шило С на мыло С++ если только за это не будут доплачивать - те же уязвимости из-за ручного управления памятью, если нужен технологически более высокий уровень - надо на Rust переходить как это делают сейчас в ядре Linux.

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


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

41 minutes ago, Arlleex said:

Да, за счет парадигмы более строгой структуризации программы нам легче ее читать,

Тут дело не только в лёгкости чтения.

Понимаю, что пример не всем покажется убедительным, но другой пока на глаза не попался. Просто был проект, написанный на ни С и ХАЛ, который написал в ООП-стиле. Поскольку у меня были наработки, то я быстро реализовал проект на С++. В итоге размер кода уменьшился в 2 раза, но при этом к нему добавилась поддержка Модбас в опостасях RTU и TCP. Кстати, разница между RTU и TCP буквально в десятке строк, когда это делается, используя ООП.

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


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

...и начался холивар (священная война) - сишники против плюсоидов, линуксоиды против винтузятников, 8-битники против 32-битников, халоводы против регистровщиков 🙂

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


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

On 9/12/2023 at 2:57 PM, tonyk_av said:

Просто был проект, написанный на ни С и ХАЛ, который написал в ООП-стиле. Поскольку у меня были наработки, то я быстро реализовал проект на С++. В итоге размер кода уменьшился в 2 раза

У меня тоже есть пример - когда переписали проприетарный драйвер GPU Vivante для Linux  размер кода уменьшился примерно в 10 раз, без плюсов.

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


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

Мне, вопреки убеждениям, вдалбливаемым в универе, что три столпа ООП это инкапсуляция, полиморфизм и наследование, по своим ощущениям кажется, что самой главной характеристикой ООП является именно полиморфизм.

Наследование и инкапсуляция к ООП весьма ортогональны, как приятный бонус, но не прямое следствие, порожденное постулатами ООП.

Т.е. самое главное, что функциональщина пишется f(x), и мы четко знаем, что делает f() и допустим ли x для ее вызова. А в ООП мы делаем x.f(), и это, помимо большей логической связки объекта и метода, дает возможность не думать, какой конкретно f() мы должны вызвать.

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


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

5 hours ago, Сергей Борщ said:

new(std::size_t, std::size_t)

(Я уже когда написал подумал "сейчас программисты накидают".  Потому что крайне любят цепляться за соринку в глазу 🙂 неточности, оставляя порой самое важное за бортом).
Да, спасибо. Я конечно же имел в виду не было отмены важных концепций.  Я бы отметил auto_ptr, который удален в C++17.  Но это не значит что удалили концепцию смартпоинтеров.
 

 

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

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


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

7 hours ago, Arlleex said:

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

Все верно. Объекты и классы должны быть максимально изолированы (это называется не иметь зависимостей, dependency-free). 
Каждый финальный рабочий объект должен представлять собой не имеющую зависимостей "изолированную" программу-объект полноценно выполняющую строго отведенные ей обязанности.

Взял объект, может быть параметризовал, сконфигурировал впрыснул зависимости — и он завёлся, начал выполнять работу. Больше ничего и не надо.
Это в идеале. К этому надо стремиться.  См. принципы SOLID
Потому давно замечено, что написанная таким образом программа приобретает целый ряд очень ценных свойств и качеств.
Одно из них — само-замкнутость, целостность, устойчивость, отсутствие хрупкости, тестируемость.

Насчет "Объекты между собой должны взаимодействовать, как ни крути"

Если CUart  (или его наследник) будет дергать методы CScreen - всё, конец. Класс-иерархия превращается в кашу взаимо-зависимостей. Потому что потом невозможно будет изъять из программы CUart и его периспользовать. CUart намертво будет переплетен с CScreen.  А должно быть написано так, что в любой момент можно изъять CUart и использоавть его в другом проекте или в другой части проекта (а иногда - прямо в программе заменить его другим классом, этому способствует паттерн "Фабрика"). 

* - я не могу здесь распылиться на агрегирование, композицию и наследование, иначе пост превратится в стену текста. Зависимости при композиции не избежать в принципе и вообще всё в конечном итоге не так просто и баинарно, есть зависимости разного рода, дальние и ближние итд.

Данный "общий" код будет "сверху", вынесен из CUart и из CScreen, содержась в некоем CDisplayManager, который в свою очередь содержит лишь экземпляры (или указатели) на CUart и CScreen. 

Это-то и будет тот, кто дергает выставленные методы объектов "из космоса" (о чем мы говорили выше).
Однако, за все надо платить. И это тоже не бесплатно, это несет проблему.  Проблемой здесь становится высокое "размытие" программы по классам и методам.
Это анти-паттерн, который можно привнести в свои же собственные программы.  Тогда вместо "цельности", "разумности" где каждый объект выполняет свое предназначение начнется иное - начнется ад из десятков классов с тонкой кодовой "кашицей" размытой между ними.  Возникает "Yo-Yo problem", когда чтобы понять или редактировать программу приходится метаться между десятками файлов и вкладок или бегать вверх-вниз по файлу.
Вдобавок, писать код подобным образом муторно. Лениво ваять дополнительные "клеевые" glue-объекты и классы (и интерфейсы). Всевозможные "CManager"-ы начинают возникать в неудержимом количестве. Но в конечном итоге платой будут определенные свойства программы.

Надо соблюдать здравый смысл. Если у себя проект небольшой, ни к чему такой пуризм.
Лучше повыкусывать потом код, чем обезуметь и самому же себе создать декомпозицию из 20 классов на 70.


 

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

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


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

"Изолированы, должны быть изолированы максимально" 🙂 И это всё? Вся "парадигма"? Мдааа, не густо, не густо. Такую фиготеку и на процедурном Си принято мутить...

Я думал, собравшиеся тут как-то более... более глубже понимают "цели и задачи" ООП, нежели как просто "изолировать всё и вся".

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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