| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > возвратить из функции void? |
| Автор: tilin 27.10.2009, 14:10 | ||
Класс А наследуется от IBase в котором есть метод Get(). В классе А этот метод переопределяется для возвращения конкретных значений. Нижеследующий пример конечно не работает, потому что возвращаемые типы Get() отличаются. А как вообще такое можно сделать.
|
| Автор: Alek86 27.10.2009, 14:23 |
| tilin, что значит "не работает"? что ты от него ожидаешь? допиши, в коде, желательно |
| Автор: azesmcar 27.10.2009, 14:26 |
Такое - никак. А что конкретно надо сделать? Можно к примеру возвращать boost::any, boost::variant. Побольше расскажи о задаче. |
| Автор: tilin 27.10.2009, 14:29 | ||
| Не работает, значит не компилируется и ругается на то, что разные типы возвращаемых значений у IBase::Get и A::Get. Чего хочется:
|
| Автор: Andrey44 27.10.2009, 14:34 |
А где же они будут одинаковыми? void!=T |
| Автор: tilin 27.10.2009, 14:46 |
Оно и понятно ) Это я для примера просто. |
| Автор: Alek86 27.10.2009, 14:52 |
| tilin, такое, как ты показал, невозможно или boost::any или dynamic_cast |
| Автор: niXman 27.10.2009, 15:02 | ||
Наверное это будет сложно для начинающего. Напиши микро-аналог variant. Что-то типа этого:
|
| Автор: bsa 27.10.2009, 15:10 |
| tilin, интересно, а как ты этим пользоваться собирался? Вот например, кусок кода не знает ничего о типе Type, для которого был в другом месте кода создан экземляр класса A<Type>... Так вот, что будет делать твой кусок кода с тем, что вернет A<Type>::Get? Я уж не говорю о том, что это даже теоретически скомпилировать нельзя. |
| Автор: tilin 27.10.2009, 15:22 | ||
Ну кратко так: A<type> создается в одном месте кода, затем помещается в массив. С массивом работает код, который может например определенным образом сортировать элементы массива, добавлять, удалять и т.д. А где-то из этого массива извлекается значение A<type> и используется. Смысл в том, что тот кто помещает в массив и тот кто извлекает из массива имеют нужную информацию о типах. А массив может содержать конечно только элементы одного типа, отсюда и наследование от единого интерфейса. |
| Автор: tilin 27.10.2009, 15:53 | ||
При получении значения static_cast до требуемого типа вполне подходит. И как я понял других вариантов, кроме как возвращать Variant, нет. Буду делать так. Спасибо. Меня только смущает, что в этом случае требуется создавать временный объект Variant в функции Get(). |
| Автор: niXman 27.10.2009, 20:37 | ||
Создай локальный в твоем классе, инициализируй его в функции Get(), и возвращай на него ссылку. |
| Автор: Lazin 27.10.2009, 20:54 | ||
можно использовать паттерн - visitor
|
| Автор: tilin 27.10.2009, 22:47 | ||
Так и сделал. Только не в Get, а сразу при создании объекта. А Get возвращает Вариант. |
| Автор: tilin 27.10.2009, 23:25 | ||
А в этом случае не придется для каждого типа добавлять новый оператор в IVisitor? Если да, то не подходит. |
| Автор: Lazin 28.10.2009, 00:01 | ||||
если они однотипные, то их можно сгенирировать с помощью шаблона если ты будешь использовать variant, то у тебя в коде будет что-то вроде -
visitor - это то-же самое, только принятие решения, о том, как обрабатывать значение ложится на компилятор (перегрузка ф-ии) и в любом случае, тебе придется написать код для разных типов(либо использовать шаблон, для нескольких похожих типов) |
| Автор: tilin 28.10.2009, 00:11 | ||||||||
Типы могут быть любые. Вариант у меня выглядит вот так:
В классе есть
В коде значение получается например так:
|
| Автор: Lazin 28.10.2009, 06:20 |
и как-же ты с ними работаешь потом, если не секрет? |
| Автор: tilin 28.10.2009, 08:52 | ||||
Вопрос не совсем понял, но например в варианте с int так:
|
| Автор: Lazin 28.10.2009, 09:24 | ||||||
во первых, здесь нужно явное приведение типа (static_cast), во вторых, если в v[0] не int - оператор приведения типа бросит исключение, мне интересно, что ты будешь делать, если у тебя в одном контейнере значения разных типов, как в твоем первом примере?
|
| Автор: tilin 28.10.2009, 09:42 | ||
Вот пример - код рабочий.
|
| Автор: Lazin 28.10.2009, 10:04 |
и бесполезный приходи, когда попробуешь написать реальный код, использующий этот класс, вопросы сразу возникнут, я гарантирую это |
| Автор: tilin 28.10.2009, 10:25 | ||
Вообще заморочки с Вариантом требовались для вполне реального кода, в продолжение поста http://forum.vingrad.ru/forum/topic-277666.html. А нужно это все для того чтобы на автомате создавать объекты хранящие вызов функции вместе с параметрами и возвращаемым значением. Вот для получения возвращаемого значения из объекта хранящего вызов и потребовалось представить его Вариантом. Потому что такие объекты наследуются от абстрактного интерфейса и помещаются в вектор. Все прекрасно работает. |
| Автор: Lazin 28.10.2009, 10:32 | ||
пока ты знаешь тип возвращаемого значения просто у тебя нет способа узнать тип значение, хранящегося в Variant-e, я уже несколько постов пытаюсь на это намекнуть
вообще, лень вникать, sry просто, если у тебя не возникает необходимости определять тип значения, хранящегося в Variant-е, значит вероятно, тебе вообще не нужен Variant, ты просто усложнил архитектуру приложения |
| Автор: tilin 28.10.2009, 10:43 |
| Нет, код который получает результат тип знает конечно. Как этот код выбирает объекты из контейнера - это другой вопрос. Но факт, что все объекты в контейнере должны иметь один тип, отсюда и наследование интерфейса, а из наличие в интерфейсе одного виртуального Get() на все наследуемые типы требует, чтобы Get() возвращал Вариант, который неявно преобразовывается к требуемому типу. |
| Автор: mes 28.10.2009, 11:59 | ||||
если тип объекта известен и помощь компилятора не требуется, то хранить объект можно и как void * как раз от такого как происходит взаимодействие с элементом контейнера и зависит какой интерфейс должен поддерживать объект. как я понимаю у Вас тип зависит от индекса, тогда если контейнер условно находится в библиотеке которая ничего не знает о типах, и предоставляет просто место для хранения, то тут как раз хранение по void* и каждый клиент имеет интерфейс по получению "готовых" объектов. если же нужно хранить разношерстные элементы как одну структуру и все типы статически известные, то посмотрите например библиотечку boost::fusion в частности : http://www.boost.org/doc/libs/1_40_0/libs/fusion/doc/html/fusion/container/vector.html В общем предположить что Вам требуется можно в разных вариантах и для каждого варианта нужен свой подход, поэтому, чтоб не гадать , было бы лучше если бы Вы полноценно осветили бы свое задание. |
| Автор: tilin 28.10.2009, 12:26 | ||||||
Абсолютно не зависит. Объекты в массив добавляются в произвольной последовательности.
В первом варианте Get() возвращал именно void*, потом клиент делал static_cast до нужного типа. Но мне хотелось, чтобы вместо
было
Конечно использовать динамическое приведение типов вместо статического не есть Гут. И я еще подумаю, какой из них выбрать. |
| Автор: mes 28.10.2009, 12:51 | ||||
так тип объекта с чем то связан или программист сам должен помнить под какой индекс что он добавил ?!
имхо, приведенной в этом топике информации недостаточно для совета. |
| Автор: tilin 28.10.2009, 13:23 |
| Общая задача выглядит так: Требовалось реализовать объект, хранящий указатель на объект некого класса, указатель на метод класса, параметры вызова и возвращаемое значение. Такие объекты формируются вместе с параметрами, помещаются в список вместе с метаданными. Список преобразовывается. Некоторые элементы перемещаются в начало, некоторые в конец и т.д. Затем по определенным условиям вызывается метод Call объектов. Возвращаемое значение сохраняется в объекте. Затем по метаданным ищется объект в списке и код получает результат вызова (точно зная его тип конечно). |
| Автор: bsa 28.10.2009, 17:37 | ||
| tilin, правильно ли я тебя понял, тебе нужно создавать "задачу", которая в большинстве случаев выглядит как вызов: result = object->method(data)? Если result всегда одного типа, то рекомендую обратить внимание на boost::bind (для создания "задач") и boost::function (для хранения "задач" в контейнере). Если разных типов, то еще и на boost::any. например, у тебя есть метод int A::method(double x). Ты хочешь его поставить в очередь задач a->method(1.0), тогда boost::bind(&A::method, a, 1.0) создаст тебе объект, который можно вызывать так: o(). Чтобы этот объект чему-нибудь присвоить (пока нет auto из c++0x) можно использовать или шаблоны или boost::function: boost::function<int()> f = boost::bind(&A::method, a, 1.0). Теперь появился функциональный объект f, который можно вызвать так: f(). А вот это уже можно хранить в контейнере (для случая с any):
|
| Автор: tilin 28.10.2009, 20:45 | ||||||||
Да надо создавать что-то типа задачи, которая хранит объект, метод, параметры вызова и возвращаемое значение. Однако результ может быть совершенно разного типа. В сторону boost-а я уже начал смотреть, но пока с целью ознакомления только лишь. Выложу свое решение, поправьте, если что не то сделал. Списки типов из библиотеки Loki. В Loki NullType.h заменено:
Основная реализация такая:
Макросы только для удобства записи. Пример использования. Func6 для примера без использования макроса SETTYPES:
|
| Автор: bsa 29.10.2009, 16:11 | ||||||
| tilin, я тебе дал готовое решение, которое в разы проще и надежней твоего - используй его! Забыл написать, как добавлять задачи. Допустим, есть менеджер задач, у которого есть метод добавления, объявленный так:
Кстати, начиная с версии 1.35 в boost уже есть менеджер задач! Вот только находится он в неочевидном месте и еще менее очевидно называется. Это boost::asio::io_service. Чтобы с ним работать нужно знать всего два метода: io_service::post() (постановка задачи на асинхронное исполнение), и io_service::run() (выполнение всех задач, пока они есть в очереди; может быть вызван одновременно в нескольких потоках, что приведет к параллельному исполнению задач). |
| Автор: Lazin 29.10.2009, 16:47 | ||||||||
а еще - функциональней, благодаря полиморфному поведению boost::function, в качестве задач можно использовать не толко ф-ии, но и методы и функторы, твой пример можно переписать вот так:
Добавлено через 4 минуты и 51 секунду
кстати, если нет желания использовать boost::asio::io_service, то можно просто сделать не шаблонный класс - наследник Task, у котрого был-бы определен виртуальный метод result, complete и operator() и работать с объектами Task - полиморфно
|
| Автор: tilin 29.10.2009, 17:26 | ||||
| Все понял. Честно, я понятия не имел, что если сделать вот так
а потом вот так
То в bind будут автоматически определены типы. Я в шоке. Век живи - век учись ) |
| Автор: bsa 29.10.2009, 18:58 | ||||||
Решил свой аналог boost::bind сделать? |
| Автор: tilin 30.10.2009, 13:12 | ||
Просто очень хочется разобраться как делаются такие вещи Вот что получяется. Я в правильном направлении?
|
| Автор: mes 30.10.2009, 14:26 |
весь код не смотрел, но судя по этой строке : имхо Вы держите путь в сторону. |
| Автор: tilin 30.10.2009, 15:16 | ||||
А вот если добавить сохранение результата в виде Варианта, тогда при получении значения руками преобразования типов делать не надо:
Только в моем варианте dynamic_cast пользуется, поэтому наверное стоит поучиться у boost::any ) В общем вот так получается
|