![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
ZeusAtVingrad |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 82 Регистрация: 12.4.2006 Репутация: нет Всего: нет |
Добрый день!
Передо мной встала такая задача (или по-крайней мере у меня сложился такой путь её решения): Есть базовый класс. Известно, что у его наследников будут функции с определёнными (одинаковыми) сигнатурами, но неизвестно сколько их будет и как они будут называться. Т.е. в базовый класс их не пропишешь ни под каким видом. В то же время, код, использующий объекты производных классов, ни о чём кроме базового класса не знает. Я объявил typedef с указателем на функцию-член БАЗОВОГО класса (с нужной мне сигнатурой - в примере void). А затем, получая указатели на функции-члены ПРОИЗВОДНЫХ классов и применяя их к указателям на объекты ПРОИЗВОДНЫХ классов - вызываю функции этих производных классов. Погонял код и так, и сяк. И с виртуальными функциями и с наследованием следующего уровня - вроде бы всё работает как задумано. В рабочей версии производные классы будут лежать в shared library, грубо говоря - вызывающий код будет получать всё: и указатель на объект и указатель на функцию - имея "на руках" только строки с названиями *.so (*.dll) и функций. Вопросы: 1. Это у меня случайно работает или всё соответствует стандартам языка и будет работать и на других платформах, на других компиляторах? 2. Вообще задумка здравая или по фен-шую правильнее было бы решать эту задачу как-то по-другому?
Выводит (как я и задумывал) следующее:
Это сообщение отредактировал(а) ZeusAtVingrad - 21.11.2013, 22:43 |
||||
|
|||||
vinter |
|
|||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 13 Всего: 56 |
Вообще говоря если соблюдать осторожность и использовать правильные функции на правильных объектах, то да, этот код должен работать на любой платформе. Но это подходит для домашних проектов, т.к. когда над проектом работают больше одного человека, то об осторожности можно забыть. Любое решение должно быть устойчивым, это решение абсолютно неустойчиво. Можно присвоить функцию из одного объекта - другому и тогда будет UB.
Если у тебя появилось желание достучаться из предка к потомку, то значит у тебя что-то не так с архитектурой. |
|||
|
||||
ZeusAtVingrad |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 82 Регистрация: 12.4.2006 Репутация: нет Всего: нет |
А если всё это завернуть в архитектуру так, чтобы программист не мог вызвать не ту функцию у не того объекта? Ограничить возможности программистам по оперированию указателями (в идеале - чтобы они этих указателей и не видели).
А как по другому можно описать множество объектов про которых известно только то, что у них есть функции с определённой сигнатурой? |
|||
|
||||
vinter |
|
|||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 13 Всего: 56 |
ZeusAtVingrad, ты попробуй описать конкретную задачу. Быть может ты вообще не туда копаешь.
|
|||
|
||||
ZeusAtVingrad |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 82 Регистрация: 12.4.2006 Репутация: нет Всего: нет |
Нужен "компот" из компонентов, подгружаемых "движком" из расширяемых библиотек в соответствии с конфигом. У компонентов - набор свойств (функции set/get).
Движок о них ничего не знает. Между свойствами устанавливаются связи-подписки: изменение свойства одного компонента рассылается в свойства других компонентов. В целом, задача стоИт такая. |
|||
|
||||
vinter |
|
|||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 13 Всего: 56 |
я всё равно ничего не понял. Нужно подробное описание, чтобы можно было что-то нормальное подсказать.
Но если надо вызывать метода по какому-то событию, то можно храить контейнер из std::function<void()>, а в контейнер напихивать нужные функции через std::bind. Если нет возможности использовать современный компилятор, то можно заменить std на boost и использовать его. Так ты избавишься от проблем с неправильным присваиванием функций. Каждая функция будет связан с правильным объектом. |
|||
|
||||
bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
ZeusAtVingrad, в этом случае обычно используется механизм подобный следующему: Базовый класс содержит контейнер свойств (std::map<const char*,std::pair<Getter,Setter> >), в который каждый потомок из конструктора добавляет свои свойства, а пользователь класса использует специальные методы базового класса для задания значений свойств.
|
|||
|
||||
ZeusAtVingrad |
|
||||||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 82 Регистрация: 12.4.2006 Репутация: нет Всего: нет |
Нужна поддержка примерно следующего (очень простой и короткий пример, для иллюстрации):
Все объекты лежат в некоем контейнере. Работают самостоятельно (у кого-то поток слушает сеть, у кого-то поток с таймером "щёлкает", кто-то получает события с графики), передают значения друг другу и всё это - работающее приложение.
Спасибо, в сторону bind'а посмотрю. Не хотелось решения типа boost::any (из-за скорости), но похоже, что всё-равно придётся использовать (и) его.
Была такая мысль. Получается, что в каждом объекте класса-наследника будет такой map? Нерационально с точки зрения использования памяти. И static его не сделаешь. Потому что для классов-наследников они всё-таки должны быть разными. И ещё: Getter и Setter - что из себя могут представлять? Опять в каком-то виде - указатель на объект и на функцию? |
||||||||
|
|||||||||
bsa |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
Есть еще один вариант: делаешь виртуальную функцию, которая принимает тип операции, название свойства и ссылку на значение. Ее переопределяешь в каждом потомке. В ее задачи входит определение свойства и вызов соответствующего метода. В случае с малым количеством свойств этот вариант может быть более оптимальным. |
||||
|
|||||
ZeusAtVingrad |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 82 Регистрация: 12.4.2006 Репутация: нет Всего: нет |
Ну так а потомки-то все его наследуют. И В КАЖДОМ объекте класса-наследника будет такой map. А нужен только один общий для каждого класса-наследника (т.е. он должен быть static в наследнике).
Не, "узловые" функции (которые потом будут раскидывать вызовы внутри) - типа marshaling такой, - я не хочу. Я всё стремлюсь сделать на указателях, причём и их-то как можно меньше. Чтобы архитектура была максимально скорострельной. |
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 60 Всего: 223 |
Вам нужны closure (с замыканием в них this и member pointer'а) и/или делегаты В С++ 11 closure наверное можно реализовать с помощью лямбда функций |
|||
|
||||
vinter |
|
||||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 13 Всего: 56 |
Лямбда и есть closure.
Косвенный вызов функции, как правило, не является узким местом. |
||||
|
|||||
vinter |
|
|||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 13 Всего: 56 |
Еще идея:
Сделать у базового класса следующую функцию:
И в каждом потомке переопределять соответствующим образом, кидая исключение, если функция не известна. |
|||
|
||||
ZeusAtVingrad |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 82 Регистрация: 12.4.2006 Репутация: нет Всего: нет |
Ну, собственно, у меня задумка так и была реализована. Только вместо std::function<void> я бы возвращал указатель на функцию класса. Тут вопрос-то больше не в том как сообщить "кому надо" указатель на функцию, а в том - что именно сообщать. Действительно ли указатель на функцию или может быть как-то по другому решить взаимодействие объектов... Углубился в изучение C++11. Раньше его не использовал. Пока непонятно как прикрутить эти лямбды к моей ситуации. |
|||
|
||||
vinter |
|
||||
![]() Explorer ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2735 Регистрация: 1.4.2006 Где: Н.Новгород Репутация: 13 Всего: 56 |
в твоей ситуации лямды прикручиваются так же как и bind, в данном случае я бы выбрал bind, т.к. лямбды избыточны.
Про указатели на функции пора забыть, это из С с классами. Тем более, что их очень легко использовать неправильно. function не даст тебя использовать неправильно ;) Добавлено @ 19:32 например так можно сделать для класса B::foo:
или через лямбду:
Это сообщение отредактировал(а) vinter - 22.11.2013, 19:33 |
||||
|
|||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |