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

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

2 минуты назад, Arlleex сказал:

Я, видимо, зря капнул эту тему, просто нет у меня тут рядом гуру плюсов

Да их и нет. Вообще. "Это фантастика". Как я замечаю - для большинства таких "гуру", написать простую программу и чтобы весила она не более мегабайта - это уже запредельная оптимизация.   :cray:

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


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

49 minutes ago, jcxz said:

Тогда с вами всё ясно.

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

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


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

Ну вот, пришел jcxz, и снова всем выставил двойки и колы. С вашего позволения выражу свое лирическое мнение 🙂

История циклична. Где то в начале 00х, когда люди массово переползали с ассемблеров на Си многие задавались вопросом - писать или не писать на ++. Тогда вероятный некоторый перерасход памяти и лишние циклы имели значение, но и тогда уже писали некоторые на ++. Сейчас памяти много, мегагерцев - еще больше и почему бы не писать на ++. И пишут, если оттого есть смысл. Но как оказалось это мало где надо во встраиваемых делах. В тех же нулевых попробовал - сделал класс 1Wire, от него произвел разные датчики, посмотрел на это все и... да, помудрёнее выглядит, работает одинаково с плоским Си, памяти отъедает столько же и нафига козе баян...

Создавать какой то свой на многие лета фрэймвок типа как сделано в MFC смысла не вижу, так как сегодня работаю с STM32, завтра PIC18, и т д. И сделать универсальное - нереально.

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

Как резюме - хочешь развиваться - осваивай открытые проекты (например сетевые стеки, RTOS, файловые системы, GUI и проч.) - а на чем они сделаны - дело второе, главное, чтобы сделано оно было хорошо, а исторические open source проекты как правило сделаны красиво хоть на С хоть на С++.

 

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


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

Проблема плюсов еще в том, что они развиваются весьма активнее, нежели Си.

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

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


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

2 hours ago, Arlleex said:

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

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

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

Кто заявляет что знает кресты лжет. Возможен только тот или иной уровень владения язком. Сейчас практически невозможно найти человека, который бы реально знал все особенности и нюансы языка в полной мере. Это разве что несколько человек, работавших над переводом стандарта и компилятором. Да и то, вряд ли они отслеживают все последние изменения.

Или вот такая иллюстрация. В коне 90х и начале 2000х я часто видел (и сам это делал) как плюсы использовали занятным образом - как своеобразный "расширенный" си.   Ведь ничто абсолютно не мешает писать на плюсах как на сях. Си это подмножество C++.  Ну будут некоторые нюансики, более жесткий контроль типов. Вместе с тем будут и приятные фичи.

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

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




 

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


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

23 минуты назад, std сказал:

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

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

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


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

Вот книга "Дизайн и Эволюция C++"  авторством Страуструпа.  1994 год.  Ее хоть и знают, но редко упоминают. Чтение позволяет постичь почему были выбраны те или иные решения.

 

 

Дизайн и Эволюция C++.djvu

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


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

51 minutes ago, std said:

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

И вот это приводит к тому, что каждый пишет на своём диалекте, читать чужой код на C++ может быть очень непросто.

 

54 minutes ago, std said:

По мере роста C-программы она все более и более начинает напоминать mudball, даже если писать модульно, обязательно h "интерфейсы", с грамотным именованием функций в виде префиксов модулей и применять объектно-ориентированное построение функций (первый параметр функции - указатель на структуру, все данные собраны в структуры и под-структуры)

Все тоже самое может быть и на C++ если нет цели поддерживать качество кода, а только бы работало. Даже наверно больше возможностей для этого.

 

56 minutes ago, std said:

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

Не было такого. То, что приходилось делать руками (и кодогенерацией), как выяснялось и в C++ тоже не имеет внятного решения. Какой-то смысл имеет RAII, перегрузка операторов, может быть исключения. Но это неточно, есть недостатки, в большинстве случаев я все равно предпочту plain C либо другой язык. Сократить количество писанины не получалось, но очень хорошо получалось увеличить количество переписываний кода, после каждого выяснения, что это надо было делать не так.

От шаблонов и STL пользы больше, но это уже другое и недостатки там тоже есть.

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


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

26 минут назад, amaora сказал:

И вот это приводит к тому, что каждый пишет на своём диалекте, читать чужой код на C++ может быть очень непросто.

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

Воот! Полностью согласен, каждый пишет как понимает, в итоге имеем 100500 разработчиков, которые не понимают код друг друга, либо срутся по поводу правильного понимания тех или иных техник или паттернов или просто конструкций применительно к какой-то задаче.

Про количество писанины, к сожалению, тоже соглашусь. У меня хоть все эти классы с namespace-ми и выглядят как-то более систематично, чем в голых Си, но объема текста и, самое страшное, мгновенного понимания "скелета" или архитектуры программы или какого-то отдельного драйвера (особенно при взаимодействии дюжины периферий между собой внутри одного файла - DMA/SPI/GPIO и т.д.) не достигаю. Т.е. в своем коде, конечно, я разбираюсь быстро, но этого чаще всего нельзя сказать о других людях.

Насчет переписываний - тоже согласен на все 100% - иногда даже не понимаешь, как правильно (по формальным правилам хорошего тона) что-то написать, несмотря на то, что оно итак работает. Язык постоянно улучшается, руки чешутся и в свои исходники плеснуть немного этих улучшений. Только обычно взращивается монстр, который потом опять переписываешь, в который раз пытаясь улучшить ("на этот раз") читаемость...

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


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

53 minutes ago, amaora said:

Не было такого.  <...>  Сократить количество писанины не получалось, но очень хорошо получалось увеличить количество переписываний кода, после каждого выяснения, что это надо было делать не так.


Со всем выше согласен. У всех разный опыт и разное понимание и даже использование языка.

Что же касается именно применения ООП, опишу случай, который у меня был в жизни.
Году в 2004 у меня был срочный и неприятный проект. Пришлось как бы "спасать" своего партнера, который взял очень важный проект, но почти что завалил его, отдав парню работавшему ранее на PHP и не имевшему даже представления о байтах.  Время было потеряно.

Проект этот представлял собой просмотрщик масок микросхем (он открывал Calma GDSII файлы) в 3D виде. При этом верхние слои представлявшие собой как правило токопроводящие дорожки должны были в простейшем случае как бы "осыпаться" вниз на нижние, изображая посредством OpenGL как в кремнии созданы структуры.
Это "осыпание" пришлось сделать логическими операциями над сложными многоугольными полигонами масок.

Но дело вот в чем. Алгоритм, который производил действия над слоями и составляющими их частями был настолько сложен, что я смог выразить его только в качестве C++ объектов над которыми осуществляли действия операторы.
Написать и отладить алгоритм не-объектно, чисто в процедурной парадигме оказалось выше моих сил.
А объектно, переопределив операторы комбинирования (or xor and not) удалось его наконец запрограммировать, потому что конечные выражения резко упростились и стали представлять собой своеобразный метаязык.
Отдельной сложностью там являлось управление памятью - вследствие операций над полигонами объекты самостоятельно множились или сокращались/удалялись.
Мне помогли такие вышестоящие абстракции как контейнеры и shared pointer'ы  (тогда они не были в стандартных библиотеках).
Такой вот опыт, когда возможности C++ позволили реализовать проект вообще как таковой.
Я конечно понимаю возможный скепсис, когда читаешь такое. Казалось бы, любая C++ программа изоморфна C-программе. Но дело-то в том что возможности человека не бесконечны. Я не говорю что C++ это прям-прям панацея и золотая пуля. Например несколько прошивок приборов я намеренно написал на чистом Си (однако же, с усложнением назрела необходимость переписать на C++).  Возможностей настрелять себе в ногу в C++ гораздо больше чем в Си.
C++ всего лишь инструмент облегчающий определенные вещи, часто он делает структуру и код понятными, убирая шум низкого уровня и оставляя application level.

 

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

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


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

37 minutes ago, Arlleex said:

самое страшное, мгновенного понимания "скелета" или архитектуры программы или какого-то отдельного драйвера (особенно при взаимодействии дюжины периферий между собой внутри одного файла - DMA/SPI/GPIO и т.д.) не достигаю. Т.е. в своем коде, конечно, я разбираюсь быстро, но этого чаще всего нельзя сказать о других людях.

Это связано с типичными архитектурными особенностями программ написанных в процедурном стиле и в ООП стиле.

1. В процедурном стиле все вызовы иерархичны (и чаще всего при правильном построении локальны).  Функция дергает другие функции.  Или эту функцию выше кто-то дергает.  Program Flow и что самое главное - логика программы легко восстанавливается.

2. А в ООП программы и пишутся обычно как симуляторы дискретного времени (как это и было в основополагающей Simula).  Есть "мир" или "набор" объектов (классов если быть более точным).  У объектов выставлены наружу методы.  А кто там дёргает за эти методы - неизвестно. Кто-то дергает... Извне..  Из космоса-пространства.  Так написаны большинство больших ООП проектов и поначалу меня это сильно смущало, до сих пор привыкнуть не могу.  Особо ярко это выражено в Java и в особо больших проектах, редактор Eclipse например. Любой public-метод кандидат.
Еще одной особенностью как ООП так  и C++ программ является очень частая передача управления по указателю, которая если используются "виртуальные методы" происходит за кулисами всегда и неявно. И наконец скрытое неявное преобразование типов.

Итогом всего этого часто является то, что хотя и известно что метод вызывается, но найти откуда он зовется чисто по коду программы невозможно. Только запускать и ставить breakpoint.


 

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


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

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

известно что метод вызывается, но найти откуда он зовется чисто по коду программы невозможно

Так ведь такая же вещь происходит и в процедурном Си. Например, глядя на ф-цию Sum(a, b) невозможно сказать, откуда она будет вызвана. Для пояснения этого есть комментарии, в частности, Doxygen оформление, которое с помощью утилиты может быть сгенерировано в html-представление. 

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

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

ну почему же. Хотябы вот NULL, замененный на nullptr.

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


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

Раз "пошла такая пьянка", то ещё пять копеек.

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

С++ является надмножеством С с натяжкой. Это таки разные ЯП с разными стандартами, есть прямые несоместимости. С++ является в известном смысле потомком С, поэтому унаследовал основы синтаксиса и процедурную парадигму программирования. Но в целом это другой ЯП, который помимо процедурной парадигмы нативно поддерживает ещё две: объектную и объектно-ориентированную.

Благодаря объектному подходу у программиста появляется возможность оперировать программными сущностями как аналогами объектов реального мира. Т.е. если программист хорошо знаком с предметной областью (а это необходимое условие для качественного результата), то он может строить программу как модель реального мира. Например, можно взять листок бумаги и нарисовать структурную схему того, что должна реализовать программа: при этом "квадратики" объектов схемы - это классы, а стрелочки, соединяющие "квадратики" - это открытые (публичные) функции-члены классов - интерфейс классов. Таким образом, фактически получается "скелет" программы, можно начинать писать код, наполняя в дальнейшем классы деталями реализации.

Этот подход разительно отличается от принятого в С. С++ своими средствами (классы, инкапсуляция и абстракция) его провоцирует, а в С нужно предпринимать усилия (как приводилось выше: размещать код в структурах, явно передавать "this" и подобный "закат Солнца вручную", а главное - нет реального сокрытия деталей реализации и отделения интерфейсов от реализации). Переход от Сишного подхода к С++ному проходит не совсем просто (у всех по-разному), тут нужна некоторая ломка стереотипов при переходе от процедурного подхода к объектному. Но потом доминирующим становится объектный, как более высокоуровневый.

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

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

 

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


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

Ой блин, сколько букав то по написали...

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

структурную схему того, что должна реализовать программа: при этом "квадратики" объектов схемы - это классы, а стрелочки, соединяющие "квадратики" - это открытые (публичные

Тоже самое делается и на процедурное Си, да и вообще на любом языке программирования. 

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


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

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

У объектов выставлены наружу методы.  А кто там дёргает за эти методы - неизвестно. Кто-то дергает... Извне..

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

Вот пример: мне нужно принять строку по UART и выдать ее на дисплей, подключенный по SPI к тому же микроконтроллеру. Хорошо, у меня будет класс работы с UART, унаследованный (каким-то образом) от базового класса последовательного порта (класса-интерфейса). Интерфейс будет иметь виртуальный метод ReadString(), который вернет принятую строку. Разумеется, класс-наследник в рамках реализации конкретного драйвера работы с UART, переопределяет ReadString(), а также расширяет свой функционал (локально), чтобы банально, например, взаимодействовать с прерываниями UART-модуля, или тем же DMA. Соответственно, прикладной код будет работать не с классом этой конкретной реализации порта, а через указатель на тип класса-родителя (интерфейс).

И это объектно-ориентированный подход. Тут сразу возникает вопрос: что (какая логика в ООП-парадигме) мешает напрямую использовать объект наследника?

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

Далее - вернемся к UART. Вот торчит у нашего абстрактного класса последовательного порта метод ReadString(). Хорошо. Таким же образом у нас будет выстроена иерархия работы с дисплеем - экранчик отнаследуется от некоего абстрактного интерфейса "поток вывода" с его виртуальным методом Print(). В дебрях же реализации класса наследника для работы с конкретным экраном тоже будет своя иерархия для подключения к шине SPI (видимо, какой-то класс-адаптер) и т.д., не суть.

А теперь: кто-то должен вызвать ReadString() и поместить считанную строку в Print() экрана. Кто? Это реализация той самой "стрелочки" между "квадратиками-классами", о которых писал выше @dxp. И это будет самый банальный функциональный стиль вызова методов - никаких дерганий извне и т.д. Только программист знает, что должна делать его программа, и вот то самое место, где он пишет логику взаимодействия  между объектами в самом незамысловатом функциональном стиле. Просто дергает нужные методы у нужных классов. Никакого ООП во взаимодействии объектов нет.
 

Цитата

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

Много было всякого, в том числе и ныне отваливается как deprecated. Однажды я уже упоминал тут про |= над volatile-объектом в C++20. Я смотрю периодически доклады с каких-то Яндекс-сходок от Яндекс-гур, они там подобных вещей много рассказывают, но только я на листик это не выписываю, чтобы сейчас весомо аргументировать. Но впечатление именно такое: пишем в C++11 так, в C++14 уже этак, в C++17 проект C++11 уже не соберется, потому что пятое, в C++20 не соберется какая-нибудь там хитроумная специализация шаблона (утрирую), как она была в C++17, потому что десятое, вот держите новый синтаксис и ограничений пачку.
 

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

(как приводилось выше: размещать код в структурах, явно передавать "this" и подобный "закат Солнца вручную", а главное - нет реального сокрытия деталей реализации и отделения интерфейсов от реализации).

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

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


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

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

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

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

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

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

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

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

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

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