Полиморфизм в ардуино?
- Войдите на сайт для отправки комментариев
Ср, 05/07/2017 - 18:34
Полиморфизма в ардуино, как я понимаю, в общем случае нет: нет STL, нет RTTI... Но хочется как-то все же иметь возможность вызывать функции производных классов по указателям на базовые.
Украл с просторов интернета (https://habrahabr.ru/post/182824/) такую вот штуку:
class RunnableInterface { private: int (*m_pfnRun_T)(void* const, int&); template<typename T> static int Run_T(void* const pObj, int &k) { return static_cast<T*>(pObj)->siRun( k ); } protected: template<typename T> void Init() { m_pfnRun_T = &(Run_T<T>); } public: RunnableInterface(): m_pfnRun_T(0) {} int siRun(int &k) { // assert(m_pfnRun_T); // Make sure Init() was called. return (*m_pfnRun_T)(this, k); } virtual ~RunnableInterface() {} }; class Test: public RunnableInterface { friend class RunnableInterface; private: int siRun(int &k) { k = m_value*2; return 0; } protected: int m_value; public: Test(int value): m_value(value) { RunnableInterface::Init<Test>(); } };
Однако, эта конструкция может работать только с фиксированным типом указателя на функции, а хотелось бы параметризовать... Параметризовать m_pfnRun_T можно только путём указания шаблонным всего класса, но тогда теряется самый смысл - пробежать по списку указателей на базовые мы уже не сможем... Есть мысли, как такое можно организовать? Выручайте, друзья!
а, дуино тут при чём?
Полиморфизма в ардуино, как я понимаю, в общем случае нет:
Неправильно понимаете. В ардуино используется полный (от слова "совсем полный") язык С++. Всё, что в нём есть, всё есть и "в ардуино".
нет STL, нет RTTI...
Почему нет? В поставку не входит, но поставить и компилировать Вам никто не мешает.
хочется как-то все же иметь возможность вызывать функции производных классов по указателям на базовые.
Кто-то или что-то мешает? Если Вы умеете это делать "не в ардуино", то сделаете и здесь. Похоже, мешает Вам то, что Вы не умеете этого делать самостоятельно (без готовых макросов), но это проблема не Ардуино, а Ваша.
Есть мысли, как такое можно организовать?
Есть. Изучить язык (С++) и реализовать. Или поставить готовое решение откуда-нибудь и скомпилировать.
Если Вы умеете это делать "не в ардуино", то сделаете и здесь.
Похоже, мешает Вам то, что Вы не умеете этого делать самостоятельно (без готовых макросов), но это проблема не Ардуино, а Ваша.
В ардуино используется полный (от слова "совсем полный") язык С++. Всё, что в нём есть, всё есть и "в ардуино"
Полиморфизма в ардуино, как я понимаю, в общем случае нет
Не надо меня пугать. Вроде есть и работает.
Да, виноват, неверно выразился... У меня же шаблонные функции. Шаблонная функция не может быть виртуальной. Нужен именно механизм полиморфного вызова шаблонных функций.
Да, виноват, неверно выразился... У меня же шаблонные функции. Шаблонная функция не может быть виртуальной. Нужен именно механизм полиморфного вызова шаблонных функций.
Простите что влезаю... но мне почему-то кажется, что вы что-то неправильно делаете, коли вам на Ардуино с ее количесвом памяти понадобились шаблонные функции.
Говорят, холодильник позволяет нам есть несвежие продукты. А ООП - быстро и красиво писать неээфективный код. В больших системах на мощных процессорах это не так заметно, но даже там иногда ужасаешься. что получается на выходе у программистов, слишком уж ушедших в абстракцию от реального мира :) А уж в Ардуино-то... оставьте ООП для учебных проектов. чтобы поражать своим кодом новичков и наивных аспиранток :)
Вообще-то ООП эффективно и на Ардуино. Но другое дело, что люди использующие ООП , привыкли не экономить память. Это как мажора заставить жить как обычный человек, все равно спустит все в первый же день.
qwone, красиво - а смысл? _obj нигде не используется
qwone, это понятно. нужно что-то типа
вы что-то неправильно делаете, коли вам на Ардуино с ее количесвом памяти понадобились шаблонные функции.
люди использующие ООП , привыкли не экономить память.
Не только память. Иногда результаты работы ООП оказываются, так сказать, зафиксированными в хардкоре. Например EMF файлы в винде. Программы разных производителей используют для создания EMF одну и ту же библиотеку GDI от MS. Но внутри файла сразу видно, где люди бездумно использовали ООП, а где думали головой. Размер файла с одной и той же картинкой может отличаться на порядок. Они даже на принтере печатаются с разной скоростью :)
b707. Вот есть такая вещь. Мы все наблюдаем рост мощности камней. Но какова бы не была мощь камня, если не уметь написать программу, то смысла в этой мощности нет. А значит нет смысла увеличении мощности камня. Но камни то по мощности растут , растут и программы. Так с переходом с Ассемблера на Си получился качественый скачек в вычислительной технике, как в железе, так и в программах. Понятно Си тоже не стоял на месте и расширялся, и это было еще до классов. Потом появились классы - новый скачек. И началась жопа. Человек остался все равно тупым. Даже классы стали для многих далеки, как до Москвы раком. Таким образом появились новые языки , понятные для недалеких пользоватей. Сейчас уже эти языки тоже выросли и набрались сложностей.
Это так преамбула. Даже Си++ уже исчерпало себя. Пришла эра Графических Интерфейсов. А без ООП создание их геморойная задача. Вот только ООП мало кто понимает. Я тоже . Да и что такое ООП именно в коде не описывается.
Так что если хотите что то путное сделать на Ардуине, нужно применять ООП. Даже если какжется что без него проще.
qwone, я почти со всем согласен. Но тут главное - вовремя остановится. Не применять ООП ради ООП. Не переборщить с абстракцией. Не ленится конкретизировать методы для производных классов, вместо использования обобщенных родительских. Не описывать задачу слишком обще, когда этот код для конкретного применения. И, этого, кстати, стоит придерживаться, даже если ресурсы вроде бы не давят...
Примерчик из векторной графики, из опыта - так для развлечения.
Скажем, класс "буква", метод "напечатать букву"
... а теперь класс "строка", метод "напечатать строку" - думаю, уже все догадались...
For "каждая буква" do напечатать_букву...
А потом удивляемся, почему код такой медленный.
qwone, это понятно. нужно что-то типа
Шаблоны я языке Си не могут быть виртуальными https://goo.gl/2dwsCj
qwone, я почти со всем согласен. Но тут главное - вовремя остановится. Не применять ООП ради ООП. Не переборщить с абстракцией. Не ленится конкретизировать методы для производных классов, вместо использования обобщенных родительских. Не описывать задачу слишком обще, когда этот код для конкретного применения. И, этого, кстати, стоит придерживаться, даже если ресурсы вроде бы не давят...
Примерчик из векторной графики, из опыта - так для развлечения.
Шаблоны я языке Си не могут быть виртуальными https://goo.gl/2dwsCj
Вот та же векторная графика. Это и есть ООП. Реализовывать ее без ОПП жесть.
Так и есть. Я делал разбор вектора на языке без ООП - все равно фактически пришлось создавать обьекты в виде структур, из них связанные списки... (цепочки), потом связанные списки цепочек :) - иначе жуть.
Но с другой стороны - пример выше с выводом строк побуквенно - я же не выдумал. В реальных файлах такое сплошь и рядом. Или, к примеру, ломаная линия, нарисованная с возвратом пера к нулевой точке после каждого сегмента. - явные издержки ООП
Забавно, что Микрософт даже запатентовал "метод оптимизации метафайлов" - заключающийся в удалении из кода тысяч ненужных инструкций повторной инициализации одинаковых обьектов :)
Инициализация, деницилизация это создание и уничтожение объекта в куче? Экономия памяти. С другой стороны можно создать еще объект типа канвас , напечатать туда текст. То что вышло за рамки не печатать. Потом на общий канвас наложить новый канвас и удалить новый канвас. Так быстрее, но больше памяти используется.
Третий день, вот,
Ну, Вы только в начале пути. Поговорим через полгода - не раньше.
Полагаю, рассчитывать на помощь в этом деле было с моей стороны несколько наивно...
А какая тут может быть помощь? Знания в Вашу голову никто не положит. Работайте. Будут конкретные вопросы - задавайте.
Мне нужна идея, принцип.
Ну, попробуйте, может поможет: №1 и №2. В первом таи люди делают свои расширения для STL - вот можетепочитать как они это делают.
Будут конкретные вопросы - задавайте.
Итак, для каждого типа-наследника RunnableInterface генерируется собственная реализация Run_T, указатель на которую сохраняется в m_pfnRun_T. Тут всё просто и понятно. Если мы переходим к шаблонному описанию параметров siRun и, соответственно, Run_T, то одного указателя m_pfnRun_T нам уже явно недостаточно, т.к. для каждого отнаследованного класса будет уже несколько перегрузок Run_T, на все из которых нам нужны указатели. И эти указатели нам надо:
1. Получить.
2. Сохранить в какой-то структуре данных (какой?)
3. Исходя из параметров шаблона
реализовать выбор нужного указателя из контейнера (как?)
Понимете, какая штука. Вы не задёте вопрос по сути задачи, т.е. Вы не говорите что нужно сделать. Вместо этого Вы привязываетесь к существующей программе и спрашиваете как переделать её для случая, для которого она изначально не планировалась.
Кроме того, "как сделать" - неконкретный вопрос. Вот, мне нужна программа для управления буровой вышкой. Ответьте-ка мне "как сделать?"
И ещё, если Вы думаете. что когда я писал
я пошутил, то таки нет. Вы полезли в вещи, которые за три дня не осваиваются. Даже если кто-то сейчас попытается объяснить Вам как делаются подобные вещи, Вы не поймёте. Изучайте, разбирайтесь. Со временем станет понятно.
Там, выше http://arduino.ru/forum/programmirovanie/polimorfizm-v-arduino#comment-2... я давал пример кода, к которому стремлюсь. Видимо, он потонул во флуде и вы его не заметили.
Сейчас это реализуется вот таким вот примерно костылем:
Но хочется более элегантной реализации. Грубо говоря, инкапсулировать каст внутри самой вызываемой функции.
Я заметил. Но я Вам объясняю, Вы не с того конца подходите. Вы хотите переделать этот код - это неправильно. Вам нужно написать другой, исходя из задачи.
Почтайте более позднюю приписку в моём предудущем посте
Вам нужно написать другой, исходя из задачи.
Почтайте более позднюю приписку в моём предудущем посте
Даже если кто-то сейчас попытается объяснить Вам как делаются подобные вещи, Вы не поймёте.
В общем, почитал Уилсона и Александреску, покурил интернеты, побился головой о стену и пришел к выводу, что без rtti задача не решается. Ну, то есть, не решается иначе как через switch-case... Жаль.
... покурил интернеты, побился головой о стену и пришел к выводу, что без rtti задача не решается. Ну, то есть, не решается иначе как через switch-case... Жаль.
Чкго Вы там покурили, что аж головой об стену биться стали? Вы, это ... не привыкайте к этой дряни.
Чкго Вы там покурили, что аж головой об стену биться стали? Вы, это ... не привыкайте к этой дряни.
Что было. Головой о стену - это несколько тупиковых попыток получить информацию о типе объекта в рантайме. Но не вышел каменный цветок...
тупиковых попыток получить информацию о типе объекта в рантайме.
Вцелом вывод верный. RTTI - чисто отладочное, оставлять его в релизе - ошибка. А использовать в МК - признак идиота не понимающего что творит. Но если Вам требуется инфа о типе переменной - скорей всего ошибка в архитектуре программы. Вероятно подход срисовали с какго перла богомерзкого, или простигосподи с пыхи.
ПС. К полиморфизму это разумеется не относится.
если Вам требуется инфа о типе переменной - скорей всего ошибка в архитектуре программы
если Вам требуется инфа о типе переменной - скорей всего ошибка в архитектуре программы
захотелось мне эдакий автоматически расширяющийся интерфейс, задающий лишь имя функции, но не количество и тип параметров.
Кто-то или что-то мешает сделать функцию с неопределённым набором параметров? Неужели ОН уже и это запретил?
Неужели ОН уже и это запретил?
он тихо жрёт попкорн и акуевает.
Кто-то или что-то мешает сделать функцию с неопределённым набором параметров? Неужели ОН уже и это запретил?
Обобщая задачу можно сказать, что есть набор событий (нажатие кнопки, таймаут, etc)
Вот так бы сразу и писал - события! А значить нужны id событий. Его и передавайте в обработчик (калбеки или обекты или что там у вас будет), а лучше сохраняйте в очереди и потом обрабатывайте. А от id события будет зависеть неявно кол-во и тип параметров. И это верно, в отличии от попытки опознать событие по типу и числу параметров (может я чего у вас не понял, но похоже что так написали). И обработчики из иерархии сразу по id распознают свои события от чужих. Остается пустяк - где хранить сами параметры. Тут багатство выбора очень большое, как понравится вобщем
опознать событие по типу и числу параметров
А от id события будет зависеть неявно кол-во и тип параметров
...
Остается пустяк - где хранить сами параметры.
Мешает мне незнание того, как вызов этой функции передать по иерархии наследования.
Так попробуйте, делов-то. нормально он передаётся.
Мешает мне незнание того, как вызов этой функции передать по иерархии наследования.
Так попробуйте, делов-то. нормально он передаётся.
опознать событие по типу и числу параметров
так тот код вобще мало чем интересен, у вас if (arr[i]->t_id==typeT1) и т.д. прям в цикле висит, что совсем не по ООПшному - инкапсуляция нарушена, только обект должен знать свои обрабатываемые id событий (кстати в общем случае не одно id, например нажатие и отпускание кнопки - два разных события, но интересны могут быть одному обработчику) А при больше двух событиях такой подход, как в #24 дает нежеланный вами свич.
Должно быть типа
for(int i = 0; i < elems; i++)
Простой и элегантный - динамически распределяем структуру под параметры каждого события, передаем указатель на неё в foo как void* а внутри приводим как выше писал и освобождаем память после обработки события. Только не считайте что фрагментация кучи - только мелкие накладные расходы. Но вопрос таки пустяковый, т.к. много вариантов решения имеет. И все годные, хотя и с недостатками.