Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как писать на С++ при создание приложений под ARM
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2, 3, 4
Hellper
Посоветуйте примеры использование С++ при создание программ под архитектуру ARM микроконтроллеров. Такие примеры, чтобы показывали удобство и силу использование ООП, и тянуло сразу садится, вникать и кодить.
спасибо
пс желательно под компилятор от keil-а или gcc

ПС извините за дублирование. неполадки связи.
Danis
Цитата(Hellper @ Jun 19 2011, 00:15) *
Посоветуйте примеры использование С++ при создание программ под архитектуру ARM микроконтроллеров. Такие примеры, чтобы показывали удобство и силу использование ООП, и тянуло сразу садится, вникать и кодить.


Довольно непонятная (по крайней мери для меня) привязка ООП к ARM архитектуре. Вам примеры программ c использованием ООП нужны?
zltigo
QUOTE (Hellper @ Jun 18 2011, 23:15) *
тянуло сразу садится, вникать и кодить.

Я бы в летчики пошел,
пусть меня научат.
Наливаю в бак бензин,
завожу пропеллер.
"В небеса, мотор, вези,
чтобы птицы пели".
scifi
Цитата(Danis @ Jun 19 2011, 00:21) *
Довольно непонятная (по крайней мери для меня) привязка ООП к ARM архитектуре.

+1.
C++ и ARM - вещи ортогональные. Точно такой же C++ позволяет писать программы и для x86, MIPS, AVR32 и т.д. без оглядки на набор инструкций процессора. Отсюда и недоумение.
haker_fox
QUOTE (Hellper @ Jun 19 2011, 05:15) *
и тянуло сразу садится, вникать и кодить.

Мне кажется, что насилие здесь, как и во многих других ситуациях, неприемлемо.
gladov
Цитата(Hellper @ Jun 19 2011, 00:15) *
Посоветуйте примеры использование С++ при создание программ под архитектуру ARM микроконтроллеров. Такие примеры, чтобы показывали удобство и силу использование ООП, и тянуло сразу садится, вникать и кодить.
спасибо
пс желательно под компилятор от keil-а или gcc

ПС извините за дублирование. неполадки связи.


С++ плохо подходит для ARM. Серьезно! Появляются накладняки на требуемую память, ООП пытается тянуть за собой в проект использование динамической памяти и еще много всяких неудобств. Зачем это Вам?

А если серьезно, то я сам пишу под АРМ на С++, но чтобы к этому прийти, надо сначала понять что такое ООП вообще, зачем оно нужно, почему иногда оно удобнее, и лишь потом, если придет просветление (для ускорения процесса можно почитать, например, про шаблоны ООП) можно пытаться прикрутить объекты к АРМам.
dxp
Цитата(gladov @ Jun 20 2011, 11:34) *
С++ плохо подходит для ARM. Серьезно! Появляются накладняки на требуемую память, ООП пытается тянуть за собой в проект использование динамической памяти и еще много всяких неудобств. Зачем это Вам?

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

Два, нет - три вопроса:
  1. Какое отношение имеет ООП к динамической памяти?
  2. Почему вы ставите знак равенства между С++ и ООП?
  3. Причём тут вообще паттерны проектирования? Какое они имеют отношение к ЯП С++?
haker_fox
QUOTE (gladov @ Jun 20 2011, 13:34) *
и еще много всяких неудобств

Можно подробнее?
Пишу только на Си++ для AVR (8 бит) да еще и в контексте ОС scmRTOS (спасибо ее создателям, а особенно уважаемому dxp!). Честно говоря, удобства типа:
1. Наследования объектов.
2. Перегрузки функций.
3. Операторов ++ и -- (если не ошибаюсь, они появились именно в Си++).
4. Более строгого контроля типов данных,
покрывают дополнительные расходы на память. Все сказанное субъективно, т.е. лично мое.
MrYuran
Цитата(haker_fox @ Jun 20 2011, 12:07) *
3. Операторов ++ и -- (если не ошибаюсь, они появились именно в Си++).

Если перегруженные, то да.
А обычные целочисленные и в обычном си были.
klen
Цитата(gladov @ Jun 20 2011, 08:34) *
С++ плохо подходит для ARM. Серьезно! Появляются накладняки на требуемую память, ООП пытается тянуть за собой в проект использование динамической памяти и еще много всяких неудобств. Зачем это Вам?
А если серьезно, то я сам пишу под АРМ на С++, но чтобы к этому прийти, надо сначала понять что такое ООП вообще, зачем оно нужно, почему иногда оно удобнее, и лишь потом, если придет просветление (для ускорения процесса можно почитать, например, про шаблоны ООП) можно пытаться прикрутить объекты к АРМам.


ужосс .. Вы меня извините, но это чуш. Вы хоть раз в ассемблер заглядывали из под компиллера выходящий? если магнитофоном забивать гвозди - то да С++ для АRМ не подходит, а в остальных даже очень. у меня почемуто в сложных проектах наоборот кода стало меньше - улучшилась структура кода.

более того, проведу натянутую аналогию - никто не мешает использовать базовый функционал языка С++ без фанатских расширений - тогда он будет похож на С со структурами. но вы еще в эти структуры поведение в виде методов занесете + конструкторы. а код будет АПСАЛЮТНО!! такой же как если бы Вы на С написали.

с утверждением '"..надо сначала понять что такое ООП вообще, зачем оно нужно" - трудно несогласится, тока вот уровень 'понимания материала' вызывает вопросы

PS. - я не люблю кошек.
- ты просто не у мееш их готовить!
Axel
Цитата(gladov @ Jun 20 2011, 07:34) *
...прикрутить объекты к АРМам.

Сразу напрашивается развитие темы: прикрутить ботинки к носкам, трусы к ... ну и так далее. ООП и АРМы - давно и независимо существующие сушности, способные тем не менее прекрасно уживаться в рамках конкретного проекта и вобщем-то без какого-либо "прикручивания". А трудности постижения духа и сути ООП ИМХО несколько преувеличены.
demiurg_spb
Цитата(Hellper @ Jun 19 2011, 00:15) *
Посоветуйте примеры использование С++ при создание программ под архитектуру ARM микроконтроллеров.
Так scmRTOS и посмотрите.
Danis
ТС, позвольте не большой, скромный вопрос к Вам. Зачем Вам С++ и ООП?
Есть какая то конкретная задача, либо просто нужно «войти» в тему ООП, пока мне не понятно. Расскажите, попробую Вам помочь, скину примеры кода в «личку» какие попросите.
GDI
ringbuffer-dlya-embedded - пример кода, и даже не один, в камментах еще примеры есть.
Hellper
Цитата(GDI @ Jun 20 2011, 17:11) *
ringbuffer-dlya-embedded - пример кода, и даже не один, в камментах еще примеры есть.


спасибо
gladov
Цитата(dxp @ Jun 20 2011, 11:32) *
Два, нет - три вопроса:
  1. Какое отношение имеет ООП к динамической памяти?
  2. Почему вы ставите знак равенства между С++ и ООП?
  3. Причём тут вообще паттерны проектирования? Какое они имеют отношение к ЯП С++?


Начну со второго: да, возможно я излишне строго приравнял C++ и ООП. Это конечно не так, я немного не то имел ввиду. Конечно, C++ дает нам много вкусностей помимо "классического" ООП. Но, согласитесь, основные достоинства языка С++ заключаются в его возможности писать ОО-код. Именно поэтому я не приравнял С++ и ООП, но поставил их совсем рядом и позволил себе в первом посте сместить акцент в сторону ООП.
Отношение ООП к динамике? Да никакого! Но во многих учебниках, а также в ВУЗах, преподается ООП (позвольте я все же буду тут писать про ООП, коль уж мой первый пост был де-факто про него) в тесной свзяке с динамической памятью, ибо так легче показать виртуализацию и полиморфизм. Поэтому и написал, что "ООП пытается тянуть за собой ....". Согласитесь, многие вещи удобнее реализовываются если использовать динамику? Однако этого лучше не делать, либо делать с умом, понимая механизмы и возможные последствия.
Паттерны? А разве плохо знать о них? А к С++ они имеют прямое отношение: их можно реализовать используя С++, а вот на С сделать то же самое гораздо сложнее.
brag
Да, стоит сделать new, как c++ тянет библиотечный malloc и еще иногда кучу всякого несовместимого. а если у вас защищенная ось, у каждого треда свой stack,data,heap, то и malloc должен быть свой(если есть, у меня нету heap пока,не нужно пока), и попробуй ему обясни куда надо класть данные. и еще многие вкусности c++ тянут...мотому я пишу C.
Хотя для графики(всяких там менюшек) лучше(удобнее) c++, мож буду юзать, когда нужно будет сложную графическую оболочку делать
а ООП и на C кодится, (какая разница, написать obj.func(x) или чтото типа class_func(&obj,x) ?), хоть и далеко ему до наворотов C++
dxp
Вынужден категорически не согласиться по всем пунктам.

Цитата(gladov @ Jun 21 2011, 13:40) *
Начну со второго: да, возможно я излишне строго приравнял C++ и ООП. Это конечно не так, я немного не то имел ввиду. Конечно, C++ дает нам много вкусностей помимо "классического" ООП. Но, согласитесь, основные достоинства языка С++ заключаются в его возможности писать ОО-код. Именно поэтому я не приравнял С++ и ООП, но поставил их совсем рядом и позволил себе в первом посте сместить акцент в сторону ООП.

Давайте наведём ясность с терминами, в частности с ООП. ООП - объектно-ориентированное программирование, в основе которого лежит описание программы в виде иерархий наследуемых объектов с виртуальными функциями (кои являются методами), с возможностью переопределять поведение методов на любом уровне иерархии. Т.е. если я просто определяю некий класс со своим представлением (данными) и интерфейсом (открытыми функциями-членами), объявляю объект[ы] этого класса и использую его[их] в программе - то это получается не ООП. Тут нет ни иерархий, ни наследования, ни методов (виртуальных функций). И даже если было бы и наследование, но без виртуальных функций, то и это было бы не ООП - т.к. ключевым свойством ООП является динамический полиморфизм - т.е. подмена поведения метода в наследнике на рантайме, а само по себе наследование этого не даёт.

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

Каждая парадигма имеет свои сильные и слабые стороны, и применять их все три надо к месту. С++ все эти три парадигмы поддерживает одинаково хорошо. И ни одна из них не порождает сколько-нибудь заметных накладных расходов в программе, чтобы их нельзя было использовать даже на 8-битных малышах, не говоря уже об АРМах. Где уместен С, там уместен и С++.

Цитата(gladov @ Jun 21 2011, 13:40) *
Отношение ООП к динамике? Да никакого! Но во многих учебниках, а также в ВУЗах, преподается ООП (позвольте я все же буду тут писать про ООП, коль уж мой первый пост был де-факто про него) в тесной свзяке с динамической памятью, ибо так легче показать виртуализацию и полиморфизм.

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

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

Цитата(gladov @ Jun 21 2011, 13:40) *
Поэтому и написал, что "ООП пытается тянуть за собой ....". Согласитесь, многие вещи удобнее реализовываются если использовать динамику?

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

В программах для РС, где присутствует GUI, это обычная практика - создавать объекты с помощью оператора new, т.к. GUI сам по себе динамичен - окна появляются и закрываются, действия пользователя на этапе написания программы не известны. Учитывая, что операция создания объекта в свободной памяти на РС не очень дорогая, этот подход широко применяется там и для других потребностей. Но и там злоупотреблять этим ни к чему. И недаром тов. Александреску даже разработал специальный быстрый менеджер памяти для использования его с небольшими объектами (он подробно описан в его книжке "Modern C++ design").

Цитата(gladov @ Jun 21 2011, 13:40) *
Паттерны? А разве плохо знать о них? А к С++ они имеют прямое отношение: их можно реализовать используя С++, а вот на С сделать то же самое гораздо сложнее.

Знать о них не плохо. Только это, вообще-то, приёмы проектирования, и реализовываться они могут на многих языках, а не только на С++. И уж нет никаких особы предпосылок к тому, чтобы считать их непригодными для реализации в embedded системах, в частности на ARM. Например, паттерн Singleton очень хорошо реализуется не то, что на ARM, но даже на AVR, и мы давно и широко его применяем.
scifi
Цитата(dxp @ Jun 21 2011, 12:47) *
Если вы посчитали, что уже использование классов - это ООП, то это ошибочное суждение. С++ - гибридный язык программирования. Он в явном виде поддерживает три парадигмы:
  1. Процедурую, когда программист разбивает код на функции (процедуры) и строит программу как совокупность вызовов процедур (точно как в традиционном С).
  2. Объектную, когда программист размещает код в компактных объектах, скрывая детали реализации с помощью инкапсуляции и абстракции и определяя интерфейс для взаимодействия объектов с внешним (для объекта) миром, а программу, строит определяя взаимодействия объектов между собой и с другими программными сущностями.
  3. Объектно-ориентированную, когда код строится в виде иерархий полиморфных классов (см выше).

Спасибо за обзор. Лично для меня он оказался весьма полезным и своевременным.
Оказывается, я обычно реализую подход №2 ("объектный"), средствами языка Си: разделение интерфейса и реализации - module.h и module.c, сокрытие данных и служебных функций - ключевое слово static.
В связи с этим вопрос: не могли бы Вы привести примеры микроконтроллерных задач, в которых "объектно-ориентированный" подход даёт ощутимые преимущества?
brag
согласен, обзор полностью верный. и применять тот или иной подход всегда надо к месту

Цитата
В связи с этим вопрос: не могли бы Вы привести примеры микроконтроллерных задач, в которых "объектно-ориентированный" подход даёт ощутимые преимущества?

пример - графический юзверь-интерфейс. я какраз щас задачку ставить буду, где сложный gui, все на том же cortex-m3, QT там не пойдет, прийдется писать свой, тут и применю с++, посему и залез в эти темку sm.gif
scifi
Цитата(brag @ Jun 21 2011, 13:33) *
пример - графический юзверь-интерфейс. я какраз щас задачку ставить буду, где сложный gui, все на том же cortex-m3, QT там не пойдет, прийдется писать свой, тут и применю с++, посему и залез в эти темку sm.gif

При всём уважении, это голословное утверждение. Делал GUI (насколько сложный - тут мнения в любом случае разойдутся) именно "объектным" способом, никакой тяги к "полиморфизму" не ощутил.
Отсюда и вопрос. Хотелось бы с обоснованием, где именно полиморфизм реально помогает.
Axel
Цитата(scifi @ Jun 21 2011, 12:39) *
...где именно полиморфизм реально помогает.

Из практики: измерительная система, реализующая несколько различных измерений параметров одного объекта. Количество и последовательность измерений определяются пользователем. Оказалось удобным сделать классы конкретных измерений производными от общего базового виртуального класса. Структура программы получилась независимой от деталей (и изменений) каждой конкретной технологии.
brag
Цитата
При всём уважении, это голословное утверждение. Делал GUI (насколько сложный - тут мнения в любом случае разойдутся) именно "объектным" способом, никакой тяги к "полиморфизму" не ощутил.

тут не полиморфизм, тут иерархия классов, а это уже ооп, а не оп. хотя и полиморфизм хорошо подходит.
покрасить кнопочку в синий цвет и покрасить окошко в синий цвет - совершенно разные процедуры, хотя их можно обьеденить в один метод "покрасить виджет"
dxp
Цитата(scifi @ Jun 21 2011, 16:39) *
При всём уважении, это голословное утверждение. Делал GUI (насколько сложный - тут мнения в любом случае разойдутся) именно "объектным" способом, никакой тяги к "полиморфизму" не ощутил.
Отсюда и вопрос. Хотелось бы с обоснованием, где именно полиморфизм реально помогает.

Это вы зря - GUI, как раз, то место, где ООП рулит в полный рост. Не знаю, как у вас было организовано, но типовая схема такая: все объекты GUI являются членами иерархии классов, где в базовых классах объявляются методы (т.е. виртуальные функции), назначение которых выполнять действия, сходные по назначению, но разные по реализации. Например, у нас есть объекты: главное меню, выпадающее/всплывающее меню, пункт меню, графическая кнопка, строка состояния и т.п., а так же есть органы управления прибором - несколько кнопок или небольшая [плёночная] клавиатура. Нам нужно организовать управление всем ворохом графических объектов, но сделать это как-то единообразно. К примеру, нужно обрабатывать кнопку Right (перемещение вправо). Для этого мы в базовом классе иерархии объявляем виртуальную (если в самом базовом, то кошерно её объявлять чисто виртуальной - pure virtual) функцю void right(). И во всех производных классах, где это необходимо, эта функция переопределяется, чтобы получить для каждого класса своё поведение.

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

Так вот, код для реализации этого будет до безобразия прост:
Код
    TWidget *focus; // указатель на активный графический элемент (виджет)

   ...

    focus = ...;   //  focus присваивается адрес графического элемента - как правило это присваивание происходит
                      // внутри методов самих виджетов

   ...

    if( is_key_clicked(GUI::KEY_RIGHT)
    {
        focus->right();
    }

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

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

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

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

В общем, в любом приложении, где есть общие по назначению, но разные по исполнению действия, уместно применять ООП - динамический полиморфизм отлично ложится на реализацию такой задачи.
Danis
dxp, как у вас терпения хватает писать такие длинные посты? wink.gif Думаю, это будет отличным стимулом и введение в ООП, многих эмбедеров форума. Недаром говорят, «Новосибирск - город математиков и программистов»
Во общем респект и уважуха!!!

P.S. может в контексте этой темы и ваших постов Страуструпа выложить?
scifi
Цитата(dxp @ Jun 21 2011, 15:58) *
В общем, в любом приложении, где есть общие по назначению, но разные по исполнению действия, уместно применять ООП - динамический полиморфизм отлично ложится на реализацию такой задачи.

Другими словами, средства полиморфизма из Си++ могут быть оправданы там, где на Си нужно было бы вводить "поле типа" или массив указателей на функции.
Неудивительно, что у меня нет к этому тяги: в моих задачах это редко встречается :-)
Ещё раз, спасибо за развёрнутое объяснение.
Danis
Цитата(scifi @ Jun 21 2011, 17:37) *
Другими словами, средства полиморфизма из Си++ могут быть оправданы там, где на Си нужно было бы вводить "поле типа" или массив указателей на функции.


Маленько не так. Полиформизм дает возможность создавать множественные определения для операций и функций, что более абстрагирует программы. Конкретное определение, которое будет использоваться, зависит от задачи. В результате в крупном проекте коэффициент повторного использования кода возрастает. Си, к сожалению такой возможности не предоставляет.
brag
dxp, спасибо большое за обзорчик, вроде все и так понятно, но очень удобно, когда все собрано в кучке. распечатаю себе, как шпаргалку/краткое пособие wink.gif
haker_fox
dxp, спасибо за шикарные ответы!!! disco.gif
Не моголи бы Вы подсказать, по какой литературе обучались? Или это опыт?
Спасибо!
andrewlekar
Расказ про ООП конечно увлекательный, но стоит учитывать, что как только вы вылезете за область применения, описанную dxp (наследование от интерфейса и поточная обработка), то тут же вся система станет крайне неустойчивой. Конкретно: множественное наследование сразу ставит крест на проекте, перегрузка функций и операторов приводит к очень хитрым багам, развесистая иерархия наследования приводит к хрупкости системы - очень высокая связность элементов... Использование паттернов не имеет отношения к С++, но в микроконтроллерах не имеет особого смысла. Куда можно в AVR засунуть синглтон?! Использование шаблонов С++ сильно тормозит компиляцию и плохо контролируется по расходу памяти.
В общем, это неправда, что С++ оправдан везде, где оправдан С.
dxp
.
MrYuran
Цитата(andrewlekar @ Jun 22 2011, 08:57) *
Использование шаблонов С++ сильно тормозит компиляцию

Вместо двух секунд аж все десять! biggrin.gif
Цитата
Не моголи бы Вы подсказать, по какой литературе обучались?

Ну как минимум ссылка на Александреску уже висит sm.gif
dxp
Цитата(andrewlekar @ Jun 22 2011, 11:57) *
(наследование от интерфейса и поточная обработка), то тут же вся система станет крайне неустойчивой.
Почему? В чём причины неустойчивости?

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
Конкретно: множественное наследование сразу ставит крест на проекте,

Поясните? И причём тут множественное наследование? О нём вообще ни слова не было сказано.

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
перегрузка функций и операторов приводит к очень хитрым багам,

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

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
развесистая иерархия наследования приводит к хрупкости системы - очень высокая связность элементов...

А голова-то на что? Средства-то надо применять осмысленно и к месту, а не ради них самих, о чём было сказано выше.

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
Использование паттернов не имеет отношения к С++, но в микроконтроллерах не имеет особого смысла. Куда можно в AVR засунуть синглтон?!

Да как обычно - чтобы избавиться от зависимости порядка создания объектов при раздельной компиляции файлов проекта. Затраты на него копеечные.

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
Использование шаблонов С++ сильно тормозит компиляцию

Да, несколько секунд лишних, обычно, приходится потратить. sm.gif

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
и плохо контролируется по расходу памяти.

Это только если использовать бездумно и не представлять себе, во что выливается та или иная языковая конструкция. На сях тоже можно начать бездумно маллоки метать направо и налево, и будет "плохо контролируемый расход памяти".

Цитата(andrewlekar @ Jun 22 2011, 11:57) *
В общем, это неправда, что С++ оправдан везде, где оправдан С.

Технически с точки зрения эффективности везде. А то, что голову надо включать, так это универсальное правило для любого ЯП. Единственное, с чем соглашусь, что С++ требует больше времени на изучение в силу много большего разнообразия и сложности самого языка. Это его единственный объективный недостаток перед С.
Danis
Цитата(andrewlekar @ Jun 22 2011, 08:57) *
В общем, это неправда, что С++ оправдан везде, где оправдан С.


Не согласен категорически, во всяком случае, C++ в программировании можно использовать и «ограничено», не зарываясь в тонкости ООП, при этом не платишь за то, что не используешь.
dxp
Цитата(haker_fox @ Jun 22 2011, 08:05) *
Не моголи бы Вы подсказать, по какой литературе обучались? Или это опыт?

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

Ну, и кореша есть, которые в плюсах "батонят" (как один из них выражается), особенно один, по сравнению с которым я просто мальчик в коротких штанишках. В общем, есть на кого равняться. sm.gif Ну, и опыт тоже, без опыта реальной работы, конечно, ничего не бывает, это как у всех.

На самом деле уровень проблем, который мы тут подняли, это в среде профессиональных программистов просто "асфальтированное шоссе", ко которому ехать легко и просто, это обычные штатные вещи, можно сказать тривиальные для них. Просто в embedded области в силу того, что С++ ещё только занимает позиции, и эти понятия, приёмы и средства языка идут в новинку. Но это и хорошо - то, что профессиональным РС программерам кажется рутиной и скукотой, для нас является прикольными фишками, которые вызывают искренний интерес и мотивируют работу. Второй момент: для нас это всё близко к железу, когда embedded программист пишет код, он всегда представляет, во что выльется реализация (если он хороший программист, конечно), поэтому чёткое понимание связи кода на С++ и его аппаратной реализации тоже пробуждает интерес. РС программисту в этом смысле хуже - от него реализация сидит слишком далеко. Кроме того, эффективное написание программ для настольных машин давно требует использования какого-нить развитого фреймворка, и программист оказывается заключённым в определённые рамки, выйти за которые он по сути не может - иначе сразу потеряет эффективность работы. Embedded программист в значительной степени свободен от этого, что предоставляет куда большие возможности для творчества. А именно творческое начало в любой работе делает эту работу интересной и желанной. Вот как-то так.

Цитата(Danis @ Jun 21 2011, 20:27) *
P.S. может в контексте этой темы и ваших постов Страуструпа выложить?

В смысле? Книжку его выложить или что?

Цитата(scifi @ Jun 21 2011, 21:37) *
Другими словами, средства полиморфизма из Си++ могут быть оправданы там, где на Си нужно было бы вводить "поле типа" или массив указателей на функции.
Неудивительно, что у меня нет к этому тяги: в моих задачах это редко встречается :-)

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

Цитата(Danis @ Jun 21 2011, 22:55) *
Маленько не так. Полиформизм дает возможность создавать множественные определения для операций и функций, что более абстрагирует программы. Конкретное определение, которое будет использоваться, зависит от задачи. В результате в крупном проекте коэффициент повторного использования кода возрастает. Си, к сожалению такой возможности не предоставляет.

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

* * *


Кстати, вспомнил ещё пример, где рулит ООП. Реально используется в текущем проекте. Уже как-то собирался выложить реализацию (правда, с другим акцентом), но всё руки не доходили. Раз уж тут пошло обсуждение, то пусть будет до кучи.

Речь идёт о передаче сообщений. Есть программа, в которой в разных её местах возникают события и нужно их как-то передавать на обработку. Причём, нужно сделать так, чтобы при необходимости добавить новый тип сообщения не нужно было перелопачивать существующий код. Иными словами, нужно, чтобы та часть программы, к которой попадает сообщение, как-то сама распознавала тип объекта-сообщения и обрабатывала его, если сообщение предназначено для неё, и игнорировала в противном случае. При этом и создание типов (классов) сообщений, и их посылка, и их приём были простыми и прозрачными для пользователя. Это чем-то похоже на технологию RTTI (Run Time Type Identification), поддерживаемую С++ и реализуемую на основе dynamic_cast<>, но в RTTI является довольно "тяжёлым" механизмом и не очень катит в embedded (к слову, не все компиляторы для МК и поддерживают это).

По организации. Данный пример взят из рабочего проекта, где есть небольшой самописный GUI, но сам принцип передачи сообщений, в общем, не зависит от того, где это применяется, и может быть обобщен на другие задачи. В данном примере есть иерархия классов, где заглавный абстрактный базовый класс выглядит следующим образом:
Код
    //--------------------------------------------------------------------------
    class TObject
    {
    public:
        virtual void on_message(TBaseMsgWrapper *msg_wrp) = 0;

    };
    //--------------------------------------------------------------------------

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

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

В общем, нужно, чтобы был простой и эффективный механизм генерации служебной информации, обозначающей тип сообщения, и эффективное средство проверки типа сообщения. В результате нам с корешем (он, кстати, тоже на элхе появляется под ником mikeT) удалось родить следующее (включение заголовочных файлов скипнуто):
Код
//------------------------------------------------------------------------------
inline uint16_t generate_id() { static uint16_t id; return ++id; }

class TBaseMsgWrapper
{
public:
    virtual uint16_t get_id() = 0;
};

template<typename T> class msg_wrapper : public TBaseMsgWrapper
{
public:
    msg_wrapper(T *msg) : body(msg), TBaseMsgWrapper() { }
    
    static  uint16_t get_class_id() { static const uint16_t id = generate_id(); return id; }
    virtual uint16_t get_id()       { return get_class_id(); }
    
    T *get_msg() const { return body; }
    
private:
    T *body;
};

template<typename T> T *check_msg(TBaseMsgWrapper *msg_wrp)
{
    if( msg_wrp->get_id() == msg_wrapper<T>::get_class_id() )
    {
        return (static_cast<msg_wrapper<T> *>(msg_wrp))->get_msg();
    }
    else
    {    
        return 0;
    }
}

template<typename T> bool send_message(T *msg, gui::TObject *dst)
{
    msg_wrapper<T> msg_wrp(msg);
    dst->on_message(&msg_wrp);
    
    return true;
}
//------------------------------------------------------------------------------

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

Далее, сообщение технически представляет собой тело сообщения - это тип, определяемый пользователем, который может быть произвольным - классом (или встроенным типом, но понятно, что идеологически правильно использовать класс или перечислимый тип - в общем, чтобы тип был уникальным), и класс-обёртку для тела сообщения - на каждый тип сообщения генерируется своя обёртка. Генерируется она на основе шаблона с аргументом в виде типа тела сообщения - шаблон msg_wrapper<>. Как видно из кода выше, этот шаблон генерирует классы, которые все являются производными от базового класса TBaseMsgWrapper, который является абстрактным базовым классом и определяет метод get_id(), который переопределяется в производных. Цель этого - чтобы в каждом производном классе эта функция возвращала уникальный идентификатор только своего класса, т.е. только соответствующего своему типу сообщений.

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

На приёме остаётся только проверить, что сообщение "свое", и если это так, то обработать его, в противном случае проигнорировать.

Использование. В приборе, для которого написана программа есть несколько кнопок управления (конкретно их 4 штуки). При нажатии на любую из них генерируется соответствующее событие, по котором формируется сообщение, посылаемое текущему активному объекту (указатель TObject *focus содержит адрес активного объекта).
Код
//------------------------------------------------------------------------------
     // где-то в заголовочном файле
    enum TKeyMessage
    {
        kmNONE,
        kmSEL,
        kmPOWER,
        kmPLUS,
        kmMINUS
    };

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

        gui::TKeyMessage key_code = gui::kmNONE;
        if( dev_key_events.sel.IsClick() )   // если кнопка "Select" нажата
        {
            key_code = gui::kmSEL;
            send_message<gui::TKeyMessage>(&key_code, focus);
        }
        if( power_key_event.IsClick() )
        {
            key_code = gui::kmPOWER;    // если кнопка "Питание" нажата
            send_message<gui::TKeyMessage>(&key_code, focus);
        }
        ...
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//
//  На приёме сообщения виджетами:
//
void TWidget::on_message(TBaseMsgWrapper *msg_wrp)
{
    gui::TKeyMessage *p = check_msg<gui::TKeyMessage>(msg_wrp);

    if(p)
    {
        switch(*p)
        {
        case gui::kmSEL:    handle(); break;
        case gui::kmPOWER:  back();   break;
        case gui::kmPLUS:   next();   break;
        case gui::kmMINUS:  prev();   break;
        default: print("Error");
        }
    }  
}
//------------------------------------------------------------------------------

Тут первым делом проверяется, соответствует ли тип сообщения ожидаемому. В частности, тут мы ждём сообщение типа TKeyMessage. Если реально сообщение будет другого типа, то функция check_msg() вернёт 0, и сообщение будет проигнорировано. Проверка сводится к банальному сравнению двух целых, что выполняется очень эффективно. Если тип сообщения соответствует ожидаемому, то будет возвращён указатель на тело сообщения, которое дальше может быть использовано по назначению. В частности, тут производится вызов функций в соответствии с информацией о нажатой кнопке прибора. Функции handle(), back(), next() и prev() являются виртуальными, изначально определены в TWidget и переопределяются в виджетах-потомках - это как раз тот случай, который был описан в предыдущем посте.

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

В общем, тут можно расширять и вверх, и вширь - т.е. создать столько типов сообщений, сколько нужно, и это всё без изменения остального (уже нередко стабильного) кода без опасности что-то поломать в нём. При этом не надо заботиться о правильности обработки - пользователь только создаёт тело сообщения, делает send_message() и check_message() в точке приёма. Всё остальное делает компилятор и сгенерированный код на рантайме.

* * *

Этот же приём был портирован на фреймворк Qt. Там особенность в том, что уже есть механизм передачи событий (QEvent) и при портировании было желание использовать его. Задумка у авторов Qt в части событий такова, что там события делятся на два типа - системные (всякие события от клавиатуры, мыши и прочего) и пользовательские. И предусмотрен идентификатор событий - целое. sm.gif Разделено там так: события от 0 до 1000 - это системные события, а от 1000 до 65535 - пользовательские. Реализация:
Код
#include <QApplication>
#include <QEvent>

//------------------------------------------------------------------------------
inline int generate_id() { static int id = QEvent::User; return ++id; }
//------------------------------------------------------------------------------
template<typename T> class msg_wrapper : public QEvent
{
public:
    msg_wrapper(T msg)
        : QEvent( static_cast<QEvent::Type>( get_class_id() ) )
        , body(msg)
    {
    }
    
    static  int get_class_id() { static const int id = generate_id(); return id; }
    
    T *get_msg() { return &body; }
    
private:
    T body;
};
//------------------------------------------------------------------------------
template<typename T> T *check_msg(QEvent *msg_wrp)
{
    if( msg_wrp->type() == msg_wrapper<T>::get_class_id() )
    {
        return (static_cast<msg_wrapper<T> *>(msg_wrp))->get_msg();
    }
    else
    {    
        return 0;
    }
}
//------------------------------------------------------------------------------
template<typename T> bool send_message(QObject *dst, T msg)
{
    msg_wrapper<T> msg_wrp(msg);
    return QApplication::sendEvent(dst, &msg_wrp);
}
//------------------------------------------------------------------------------

Использование. Тело сообщения - событие переименования элемента комбобокса:
Код
class TComboBoxRenameEvent
{
public:
    TComboBoxRenameEvent(QString NewValue): Value(NewValue){ }

    QString value() const { return Value; }

private:
    QString Value;
};


Сам класс комбобокса - в нём нужно переопределить функцию-обработчик события customEvent() (виртуальная).
Код
class TComboBox : public QComboBox
{
    Q_OBJECT

public:
    explicit TComboBox(QWidget *parent = 0) : QComboBox(parent) { }

    TComboBox & operator=(const TComboBox & );

protected:
    virtual void customEvent(QEvent *Message);
};


Генерация и отсылка сообщения (по кнопке Ok в диалоге переименования генерируется сигнал, к которому подконнекчен слот, представленный ниже):
Код
void TRenameDialog::on_pbOk_clicked()
{
    TComboBoxRenameEvent msg_body(ui->leName->text());
    send_message<TComboBoxRenameEvent>(Dst, msg_body);
}


И, наконец, приём и обработка сообщения:
Код
void TComboBox::customEvent(QEvent *Message)
{
    TComboBoxRenameEvent *msg = check_msg<TComboBoxRenameEvent>(Message);
    if( msg )
    {
         ...; // обработка сообщения
    }
}
//------------------------------------------------------------------------------

В общем-то, сама по себе идеология передачи событий в Qt очень похожа на описанную выше для embedded системы, она проста и логична, будучи основанной на передаче события в виде указателя на базовый класс. Но тут есть недостаток - приходится руками генерировать идентификатор типа, руками проверять на соответствие и делать явное преобразование типов (а явное преобразование типов - это, как правило, не гуд, и как утверждает тов. Страуструп: "...обычно указывает на ошибки проектирования", поэтому всегда желательно каждый такой случай рассматривать внимательно и при малейшей возможности прятать с глаз долой; в любом случае нехорошо, когда пользовательский код пестрит явными преобразования типов). Описанный приём просто все эти действия автоматизирует и предоставляет программисту более простой и безопасный интерфейс для использования.
andrewlekar
Цитата
Поясните? И причём тут множественное наследование? О нём вообще ни слова не было сказано.

С++ позволяет множественное наследование, вот и упомянул. Просто перечисление объективных недостатков языка.

Цитата
К очень хитрым (уж куда более хитрым) багам приводит неаккуратная работа с памятью и невнимательное использование адресной арифметики, а это чисто сишные вещи.

Адресная арифметика - это также и чисто микроконтроллерная область. И хоть вы на чём пишите, залезать в неё придётся. А неаккуратная работа с памятью более вероятна как раз в С++, потому что там куда чаще встречается динамическое размещение объектов.

Цитата
Да как обычно - чтобы избавиться от зависимости порядка создания объектов при раздельной компиляции файлов проекта. Затраты на него копеечные.

Ага, сначала создавать себе проблемы, используя С++, а потом мужественно их решать sm.gif

Цитата
А то, что голову надо включать, так это универсальное правило для любого ЯП.

На С++ голову уж слишком сильно нужно включать. Язык должен ограничивать программиста в его бурных устремлениях, а чрезмерная гибкость С++ позволяет выстрелить себе в ногу и успешно завалить проект. С со статической аллокацией - вот правильный выбор программиста микроконтроллеров.
haker_fox
QUOTE (andrewlekar @ Jun 22 2011, 13:57) *
В общем, это неправда, что С++ оправдан везде, где оправдан С.

Похоже, что назревает очередной спор (VS).
Danis
Цитата(andrewlekar @ Jun 22 2011, 11:38) *
А неаккуратная работа с памятью более вероятна как раз в С++, потому что там куда чаще встречается динамическое размещение объектов.


Набрался терпения и решил немного поработать писателем и корректором. Да, действительно адресная арифметика и динамическое размещение памяти при неопытности «да и опытности» тянет за собой источники потенциальных ошибок. Microsof, например, для решения этой проблемы и не только, разработал платформу .NET, под которую можно писать приложения на многих языках программирования, в частности применять C++ с расширением управляемости. (С++.Net). Код здесь, выполняется под управлением обще языковой среды выполнения CLR (Common Language Runtime), называется «управляемым кодом». Программы состоят из кода и данных, и обще языковая среда выполнения CLR обеспечивает поддержку и управляемым данным, и управляемому коду. Управляемые данные размещаются в управляемой динамически распределяемой области памяти (куче), которая имеет весьма ценную особенность — автоматическую сборку мусора (garbage collection). Если при создании программ на обычном C++ программист должен сам создавать средства управления динамически распределяемой областью памяти, то обще языковая среда выполнения CLR реализует этот процесс, отслеживая ссылки на объекты и автоматически освобождая ресурсы, занятые объектами, которые стали недоступны программе. Фактически, управляемые и неуправляемые (к которому мы все так привыкли) код и данные на C++ могут быть определены в одном и том же исходном файле, и в некоторой степени эти два мира могут взаимодействовать. Хотя использование управляемого кода и данных имеет много преимуществ (пример выход за пределы массива, на этапе run-time сразу выбросит исключение) оно может привести к снижению производительности и потере гибкости. Поэтому во многих случаях неуправляемый C++, при опытности и знаниях, оказывается лучшим выбором для создания программ.
haker_fox
По сравнению с уважаемым dxp, я вообще не программист crying.gif Но, тем не менее, позволю изложить и свою точку зрения.

QUOTE (andrewlekar @ Jun 22 2011, 16:38) *
Адресная арифметика - это также и чисто микроконтроллерная область

А к чему это уточнение? С адресами на любой платформе работать приходится.
QUOTE (andrewlekar @ Jun 22 2011, 16:38) *
А неаккуратная работа с памятью более вероятна как раз в С++, потому что там куда чаще встречается динамическое размещение объектов.

Не обязательно на Си++ размещать объекты динамически. Я не размещаю. А если размещаете, то нужно делать это корректно.
QUOTE (andrewlekar @ Jun 22 2011, 16:38) *
На С++ голову уж слишком сильно нужно включать.

Ну так это инструмент профессиональный rolleyes.gif
QUOTE (andrewlekar @ Jun 22 2011, 16:38) *
Язык должен ограничивать программиста в его бурных устремлениях, а чрезмерная гибкость С++ позволяет выстрелить себе в ногу и успешно завалить проект. С со статической аллокацией - вот правильный выбор программиста микроконтроллеров.

А почему Вы считаете, что разработчики компилятора знают лучше, что мне можно, а что - нельзя. В этих случая Pascal нужно выбирать. Он ограничивал.
Dima_G
Цитата(Hellper @ Jun 19 2011, 03:15) *
Такие примеры, чтобы показывали удобство и силу использование ООП, и тянуло сразу садится, вникать и кодить.


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

Вот реальный пример использования. Системный сервис рассылает всем существующим сервисам (потомкам base_service) какие-то синхронизационные сообщения. Системный сервис ничего не знает о количестве потомков базового. Базовые - ничего о системном. Единственное, что их связывает - тип сообщения (ClSystemMessage), который один может отправить, а другой - получить


Код
base_service.h
///@class ClBaseService
///@brief Base class for services.
/// All classes, which inherited from ClBaseService will receive message from SystemService
class ClBaseService: public TClMulticastHandler<ClSystemMessage>
{
* * *
    ///@brief Called when receive message from SystemService
    ///@param clSend_ - message from SystemService
    int ProcessMulticast(ClSystemMessage& clSend_);
* * *
};



Код
system_service.h

///@class ClSystemService
///@brief System service of environment.
class ClSystemService: public TClSingleton<ClSystemService>
{
  friend class TClSingleton<ClSystemService>;
* * *
  public:
    ///@brief Send INIT signal to all services
    ///@return Count of detected errors
    int InitServices()
    {
      DebugOut("[SystemService] Init services:\n");

      // Send INIT message
      ClSystemMessage clInitMessage_(ClSystemMessage::E_INIT);
      int i32ServiceCount_ = TClMulticastEventMech<ClSystemMessage>::EmitEvent(clInitMessage_);
      
      DebugOut("**[Init %i services, %i errors]\n\n", i32ServiceCount_, clInitMessage_.u32ErrorCounter_m);
      return i32ServiceCount_;
    }
* * *
};


Прошу прощения за "портянку", почему-то не удается ни архив, ни заголовочник прикрепить
Код
#ifndef __MULTICAST_EVENT_HEADER_H__
#define __MULTICAST_EVENT_HEADER_H__

#include "sync_obj.h"
#include "singleton.h"

#include <stdint.h>

namespace ENV
{

template <class SEND_MSG>
class TClMulticastEventMech;

///@class TClMulticastHandler
///@brief Parent class for objects, which want use multicast messages
template <class SEND_MSG>
class TClMulticastHandler
{
  friend class TClMulticastEventMech<SEND_MSG>;
  protected:
    /// Protected constructor
    TClMulticastHandler();
    /// Protected virtual destructor
    virtual ~TClMulticastHandler();
    
    ///@brief Abstract method for message receiption.
    /// Will be called when someone send message with appropriate types
    /// @param clSend_ - parameter type SEND_MSG. Read only
    /// @return can't defined. Value may be used in users purposes
    virtual int ProcessMulticast(SEND_MSG& clSend_)= 0;
};


///@class TClMulticastEventMech
///@brief Pool of requestors for one-type messages
template <class SEND_MSG>
class TClMulticastEventMech:public TClSingleton<TClMulticastEventMech<SEND_MSG> >
{
  typedef       TClMulticastHandler<SEND_MSG> THandler;
  friend class  TClSingleton<TClMulticastEventMech<SEND_MSG> >;
  friend class  TClMulticastHandler<SEND_MSG>;

  public:
    ///@brief Max count of handlers for one-type message
    static const uint32_t MAX_HANDLERS = 32;
    
    ///@brief Send message to all registered requestors
    ///@param pclSend_ - pointer to const message
    static int EmitEvent(SEND_MSG& clSend_)
    {
      return TClMulticastEventMech<SEND_MSG>::Instance()->EmitEvent_nonstatic(clSend_);
    }                      
    
    ///@brief Send message to all registered requestors, except sender
    ///@param pclSend_ - pointer to const message
    ///@param pclSender_ - pointer to sender
    static int EmitEvent(SEND_MSG& clSend_, THandler* pclSender_)
    {
      return TClMulticastEventMech<SEND_MSG>::Instance()->EmitEvent_nonstatic(clSend_, pclSender_);
    }
    
    ///@brief Send message to all registered requestors, except sender
    ///@param pclSend_ - pointer to const message
    ///@param pclSender_ - pointer to sender
    static int EmitEvent(SEND_MSG& clSend_, void* pclSender_)
    {
      return TClMulticastEventMech<SEND_MSG>::Instance()->EmitEvent_nonstatic(clSend_);
    }

      
  private:
    ///Pool of requestors
    THandler* apclHandlers_m[MAX_HANDLERS];

    ///Count of registered requestors
    uint32_t      u32CountHandlers_m;
  
    ///Private constructor. Create object can only TClSingleton
    TClMulticastEventMech():u32CountHandlers_m(0){}
    
    ///Register reqiestor
    ///@param pclHandler_ - pointer to requestor object
    int Register(THandler* pclHandler_);

    ///Unregister requestor
    ///@param pclHandler_ - pointer to requestor object
    int UnRegister(THandler* pclHandler_);

    ///Hide copy constructor  
    TClMulticastEventMech(const TClMulticastEventMech&){}              
    
    ///@brief Send message to all registered requestors
    ///@param pclSend_ - pointer to const message
    ///@param pclRecv_ - pointer to message (usually use as reply)
    inline int EmitEvent_nonstatic(SEND_MSG& clSend_)
    {
      //Process all requestors
      for (uint32_t i=0; i<u32CountHandlers_m; ++i)
        apclHandlers_m[i]->ProcessMulticast(clSend_);
      return u32CountHandlers_m;
    }                  
    
    ///@brief Send message to all registered requestors, except sender
    ///@param pclSend_ - pointer to const message
    ///@param pclSender_ - pointer to sender
    inline int EmitEvent_nonstatic(SEND_MSG& clSend_, THandler* pclSender_)
    {
      //Process all requestors
      for (uint32_t i=0; i<u32CountHandlers_m; ++i)
        if (apclHandlers_m[i] != pclSender_)
          apclHandlers_m[i]->ProcessMulticast(clSend_);
          
      return u32CountHandlers_m;
    }
};


////////////////////////////////////////////////////////////////////////////////    
template <class SEND_MSG>    
  int TClMulticastEventMech<SEND_MSG>::Register(TClMulticastEventMech::THandler* pclHandler_)
  {
    TClCriticalSection<true> clCS_;

    //Check overfull requestors list
    if (u32CountHandlers_m == MAX_HANDLERS)
      Halt("TClMulticastEventMech::Register(): Count of handlers are big");

    apclHandlers_m[u32CountHandlers_m] = pclHandler_;
    return ++u32CountHandlers_m;
  }

////////////////////////////////////////////////////////////////////////////////
template <class SEND_MSG>
  int TClMulticastEventMech<SEND_MSG>::UnRegister(TClMulticastEventMech::THandler* pclHandler_)
  {
    Halt("TClMulticastEventMech::UnRegister: Unsupported!\n");
    return 0;
  }      


////////////////////////////////////////////////////////////////////////////////
template <class SEND_MSG>
  TClMulticastHandler<SEND_MSG>::TClMulticastHandler()
  {
    TClMulticastEventMech<SEND_MSG>::Instance()->Register(this);
  }
  

////////////////////////////////////////////////////////////////////////////////
template <class SEND_MSG>
  TClMulticastHandler<SEND_MSG>::~TClMulticastHandler()
  {
    TClMulticastEventMech<SEND_MSG>::Instance()->UnRegister(this);
  }


} //namespace ENV

#endif //__MULTICAST_EVENT_HEADER_H__
Danis
Цитата(andrewlekar @ Jun 22 2011, 11:38) *
С со статической аллокацией - вот правильный выбор программиста микроконтроллеров.


С ОЗУ до 4kBytes - да, но новые МК имеют уже под сотню kBytes, как не вертись, тут увы, Вы не правы!
sergeeff
Цитата(andrewlekar @ Jun 22 2011, 10:38) *
Адресная арифметика - это также и чисто микроконтроллерная область. И хоть вы на чём пишите, залезать в неё придётся. А неаккуратная работа с памятью более вероятна как раз в С++, потому что там куда чаще встречается динамическое размещение объектов.


Ага, сначала создавать себе проблемы, используя С++, а потом мужественно их решать sm.gif


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



Все ваши высказывания от недостаточного знания и опыта работы с С++.

1. По поводу статической аллокации в С. И что тут такого сильно удобного? А если памяти не хватает все сразу и навсегда разместить? Не сталкивались с таким?

2. В С++ ошибки при работе с динамической памятью как раз резко сократились за счет того, что многие вещи делает компилятор автоматически (конструктор/деструктор).

3. Сам потратил много сил на перевод фирменных изделий с концепции С со статическим размещением переменных на С++ со всеми его вкусностями. Да, требуется время для продумывания концепции, реализации и отладки своих библиотек классов. Зато потом наступает полная лепота при необходимости расширения функционала устройства. Быстро и просто.

4. Насчет ненадежности работы устройств при их реализации на С++. Совершенно ничем необосновано. У нас устройства как раньше работали месяцами без перезагрузки, так и сейчас работают.
andrewlekar
Цитата(haker_fox @ Jun 22 2011, 14:32) *
А почему Вы считаете, что разработчики компилятора знают лучше, что мне можно, а что - нельзя. В этих случая Pascal нужно выбирать. Он ограничивал.

Потому что разработчики компиляторов умные, а программист только-только Hello World освоил. sm.gif Pascal - это хорошо, но лучше Java или .NET. Вообще во всём мире С++ верно помирает, а вы наоборот в контроллеры его суете.

Цитата(sergeeff @ Jun 22 2011, 14:44) *
Все ваши высказывания от недостаточного знания и опыта работы с С++.

На С++ я успел поработать и мне не понравилось. И шаблоны, и ОО, и перегрузки - основные фичи С++ - разводят страшную грязь в коде. Да, при хорошем опыте программист научается ходить по этим граблям, но новый программист в проекте будет разбираться очень долго. sm.gif

Цитата(sergeeff @ Jun 22 2011, 14:44) *
1. По поводу статической аллокации в С. И что тут такого сильно удобного? А если памяти не хватает все сразу и навсегда разместить? Не сталкивались с таким?

2. В С++ ошибки при работе с динамической памятью как раз резко сократились за счет того, что многие вещи делает компилятор автоматически (конструктор/деструктор).

Один говорит, что статическая аллокация хороша при малом объеме ОЗУ. Другой наоборот. Да, бывает, что без динамической аллокации не обойтись - тогда приходится идти на компромисс.
Что-то я не слыхал, чтобы в С++ где-то резко сократились ошибки памяти. Наоборот, прибавилось ошибок. Утечки памяти - фирменная фича неуправляемого С++. В бусте напихали целую кучу умных указателей для борьбы с утечками и всё равно регулярно где-то подтекает на больших проектах.
MrYuran
Цитата(andrewlekar @ Jun 22 2011, 13:00) *
И шаблоны, и ОО, и перегрузки - основные фичи С++ - разводят страшную грязь в коде.

А я так думаю наоборот - прячут с глаз долой всё лишнее и второстепенное, мешающее целостному восприятию кода.
shreck
Цитата(andrewlekar @ Jun 22 2011, 16:00) *
... но лучше Java или .NET...

... И шаблоны, и ОО, и перегрузки - основные фичи С++ - разводят страшную грязь в коде. Да, при хорошем опыте программист научается ходить по этим граблям, но новый программист в проекте будет разбираться очень долго. sm.gif

Так и шаблоны и перегрузки и ОО есть и в вашем любимом .NET. Получается, что в С++ эти фичи разводят грязь, а в .NET все чистенько? Это при том, что синтаксис C# очень близок к С++.
zltigo
История моей жизни. Когда-то очень давно, когда компьютеры были большие, микроконтроллеры были 8080, а персональный компьютер был IBM PC/XT с тактовой частотой аж 4.77 MHz.... Но тем не менее фирма Borland выпустила для персоналки первый компилятор C++ V1.0. Я, имея приличный опыт писания на FORTRAN и немного на ASM (хотя наверное приличный, потому-что BOIS для XT у которой сдохла оная пззушка я таки написал), решил воспользоваться плюсами дабы написать программатор РФ-ок. Естественно с окошками, редакторами и прочим. Окошки, конечно, не GUI, а текстовые. Рядом был программист имеющий достаточно большой для тех времен опыт писания на C, и очень отговаривавший меня от C++ - типа громоздко, криво, тормозаааа..... Но я был упорным sm.gif. Сидел, грыз книгу, думал, проникался.... Кончилось все спором, кто напишет оконную библиотеку более быструю ( тогда на XT скорость прорисовки можно было вообще наблюдать невооруженным глазом sad.gif ) компактнную и за ограниченное время.
Спор при все своей опытности коллега проиграл - уж больно все окошки на плюсы хорошо легли sm.gif. Потом правда отыгрался - полез в наглую ASM встави делать, подчищать...
Я правда в тоже туда-же. В общем библиотечка удалась и использовал я ее долго....Как и C++. Потом пришло осознание того, что Борлондячий компилятор дерьмо редкое, пришлось переходить на другие, пришлось использовать С, поскольку времена были стародавние и плюсовых компиляторов было немного sad.gif. Как-то получалось так, что я начал писать в даже на С++ в посконном С стиле, дабы портировать можно было-бы без больших переделок. В общем, я сейчас на плюсах пишу редко, но я не представляю себе, как-бы я писал на C не пройдя школу С++. Там есть масса вещей и подходов которые НЕОБХОДИМО осознать, понять и использовать вне зависимости от языка. Еще скажу, что компиляторы (особо С++) в те времена были реально неумные и ручками на С их при ДОСТАТОЧНОМ ОПЫТЕ можно было таки уделать. Все не стояло на месте - на С++ компиляторы теперь грех жаловаться. Коллеги! Учите и проникайтесь идеями С++, даже если потом будете на BASIC писать.
SergeyDDD
Цитата(andrewlekar @ Jun 22 2011, 12:00) *
Что-то я не слыхал, чтобы в С++ где-то резко сократились ошибки памяти. Наоборот, прибавилось ошибок. Утечки памяти - фирменная фича неуправляемого С++. В бусте напихали целую кучу умных указателей для борьбы с утечками и всё равно регулярно где-то подтекает на больших проектах.


Вы реально пишите глупости...
Очевидно что для Вас вопрос выбора между С и С++ это вопрос религии и только
Поэтому я бы Вам советовал быть более объктивным уж если Вы пытаетесь давать определенные оценки

А если у Вас был неудачный опыт с С++, ну тогда думаю сами знаете что "хорошему" танцору мешает

А "ошибки памяти", "Утечки памяти" и прочее прочее это "фирменная фича" неаккуратных и безграмотных "специалистов" а не "неуправляемого С++"
haker_fox
QUOTE (andrewlekar @ Jun 22 2011, 18:00) *
Потому что разработчики компиляторов умные, а программист только-только Hello World освоил. sm.gif

if(разработчики_компилятор != программисты)
{
мир_в_хаосе_и_я_ничего_не_понимаю_в_нем();
panic_kernel(&мой_мозг);
}

Дайте маленькому ребётенку острый нож, и получите тот же эффект: ребенок будет считать нож злом и говорить всем, что ножом нельзя пользоваться rolleyes.gif
QUOTE (andrewlekar @ Jun 22 2011, 18:00) *
Вообще во всём мире С++ верно помирает, а вы наоборот в контроллеры его суете.

Да? А мужики-то не знают rolleyes.gif Может будем в МК вставлять .NET и Java сразу?
QUOTE (andrewlekar @ Jun 22 2011, 18:00) *
На С++ я успел поработать и мне не понравилось. И шаблоны, и ОО, и перегрузки - основные фичи С++ - разводят страшную грязь в коде. Да, при хорошем опыте программист научается ходить по этим граблям, но новый программист в проекте будет разбираться очень долго. sm.gif

Это все субъективно. Есть люди и с противоположным мнением. Кстати, не каждый новый программист код на чистом Си разберет.


Я бы скромно предложил закрыть тему, как воинствующую)))

QUOTE (zltigo @ Jun 22 2011, 20:14) *
хотя наверное приличный, потому-что BOIS для XT у которой сдохла оная пззушка я таки написал

Мое Вам уважение! Я дальше самописного бутлоадера (на нулевой дорожки дискеты) и попытки написать примитивную миниОС в защищенном режиме IA32 не ушел. Но это было в далеком 2004 году, сейчас я немного взрослее стал))) Хотя, надо признаться, опыт был неплохой получен. Также делал окна в текстовом режиме, кнопочки и т.д. и т.п. Все на ассемблере...
Danis
Кстати, прорабатывается новая версия стандарта С++ - это С++ 0x, главной задачей которого является развитие языка С++. Вот тут впринципе не плохо описано, можно пичитать: тыц
MrYuran
Цитата(Danis @ Jun 22 2011, 15:31) *
Кстати, прорабатывается новая версия стандарта С++ - это С++ 0x, главной задачей которого является развитие языка С++. Вот тут впринципе не плохо описано, можно пичитать: тыц

Ну, наконец-то!
Цитата
В стандартном C++ для перебора элементов коллекции требуется масса кода. В некоторых языках, например, в C#, есть средства, предоставляющие «foreach»-инструкцию, которая автоматически перебирает элементы коллекции от начала до конца. C++0x вводит подобное средство. Инструкция for позволит проще осуществлять перебор коллекции элементов:
Код
int my_array[5] = {1, 2, 3, 4, 5};
for(int &x : my_array)
{
  x *= 2;
}

Эта форма for, называемая в английском языке «range-based for», посетит каждый элемент коллекции. Это будет применимо к C-массивам, спискам инициализаторов и любым другим типам, для которых определены функции begin() и end(), возвращающие итераторы. Все контейнеры стандартной библиотеки, имеющие пару begin/end, будут работать с for-инструкцией по коллекции.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.