Войти
ПрограммированиеФорумОбщее

Указатели на функции-члены классов (комментарии) (4 стр)

Страницы: 13 4 5 617 Следующая »
#45
17:34, 21 фев. 2013

Sergio
> Концепция сигналов и слотов постороена на этой технике.
Очень громкое утверждение. Это всего лишь вариант реализации, не самый лучший для плюсОв.


#46
20:23, 21 фев. 2013

glap
> Абстрагироваться от сигнатуры функции можно, но либо свой RTTI либо
> dynamic_cast надо использовать, что не есть хорошо.


Может быть я не совсем правильно понял мысль. Что значит "абстрагироваться от сигнатуры" ? Я довольно долгое время посвятил делегатам, и ни разу мне не пришлось использовать dynamic_cast


Представьте себе такое использование:

Some obj;
Delegate<void(int,int)> delegate(obj);
delegate(10,10);

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

У вас есть идеи, как реализовать такой дизайн?

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

Но если же вы укажете адрес цели:

Some obj;
Delegate<void(int,int)> delegate(obj, &Some::Foo);
delegate(10,10);

То вы поимеете сразу два дополнительных нюанса:
1. Информация о сигнатуре функции будет явно излишней. С таким же успехом можно написать просто:

Some obj;
Delegate delegate(obj, &Some::Foo); //Сработает автоматический вывод сигнатуры функции из аргумента: указателя на функцию
delegate(10,10);

Нет ни одной причины пользователю уродовать свой код шаблонами. Все необходимые данные умный механизм сам вытряхнет из типов аргументов.

2. Вы прибиваетесь жесткими гвоздями к функции цели. Максимум, что вы сможете сделать - это заменить объект, для которого будет вызван метод на объект того же класса.
Но это означает, что вы не смогли избавится от зависимости от сигнатуры функции.

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

#47
21:27, 21 фев. 2013

Kartonagnick
> Может быть я не совсем правильно понял мысль.

Скорее всего. Мой ответ был на следующее замечание:
> Во-первых он нарушает принцип "единственного типа данных", что делает работу с ним неудобной. Поскольку конкретные инстансы шаблона - это разные типы.

Имеется ввиду невозможность уйти от описания интерфейса функции на принимающей делегат стороне, что часто не удобно. Бывают случаи, когда до рантайма количество и тип передаваемых аргументов может быть не известен. Редкость конечно для си++ кода, но я сталкивался.

#48
21:43, 21 фев. 2013

glap
> Имеется ввиду невозможность уйти от описания интерфейса функции на принимающей
> делегат стороне, что часто не удобно. Бывают случаи, когда до рантайма
> количество и тип передаваемых аргументов может быть не известен. Редкость
> конечно для си++ кода, но я сталкивался.


Мой коннектор исповедует такой дизайн:

Connector con(объект, &класс::метод, аргументы_скоторыми_вызвать);

Он позволяет нацеливаться на свободные функции или методы, и сохранять заранее аргументы, с которыми нужно будет произвести запуск функции (если это нужно. Это не обязательно)

При этом, его можно запустить:

con(аргументы_скоторыми_вызвать); //функционал функтора. Фактически, означает запуск удаленной функции.
con(); //запуск либо для функции без аргументов, либо если при создании аргументы были указаны


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

Data obj;
Connector getData(obj,&Data::GetData);

...

Agregat.Getter = getData; //агрегат ничего не знает о том, откуда будут добываться данные

внутри себя он просто сделает :  typeVal = Getter ();
и получит данные откуда то извне

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


Допустим, агрегату пофику откого придут данные, но он знает, что получит некий зэт, отправив икс и игрек:

int z = Getter(x,y);  //запустится метод неведомого класса, принимающий x  и y, и возвращающий z

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

При этом, что именно будет запущено где-то там - становится не важным.


К сожалению всякие std::function, и им подобные не располагают такими возможностями. Ибо нарушают правило "единственного типа"

#49
22:07, 21 фев. 2013

Kartonagnick
> К сожалению всякие std::function, и им подобные не располагают такими
> возможностями.
статическая типизация ведь. можно всюду использовать тип Any, который хранит значения любого типа, но зачем это надо.

#50
22:28, 21 фев. 2013

LuckyMan
> статическая типизация ведь. можно всюду использовать тип Any, который хранит
> значения любого типа, но зачем это надо.

К чему это было сказано?

#51
22:38, 21 фев. 2013

Kartonagnick
> Важно - что бы сигнатура функции, ни которую нацелен коннектор совпадала с
> ожиданиями вызывающей стороны.

Вот в этом месте по хорошему и нужно динамическое приведение типа для аргументов.
Если в делегате сидит double, а вызывающая сторона туда суёт int, то надо бы эту ситуацию нормально обработать.
Может я чего-то не улавливаю?

#52
22:42, 21 фев. 2013

Kartonagnick
> К чему это было сказано?
как я понимаю, твой Connector  проверяет типы параметров на этапе исполнения, по-моему, лучше, чтоб проверка была на этапе компиляции как у std::function.

#53
22:48, 21 фев. 2013

На этапе исполнения, как я понимаю, много не проверишь. Можно понять, что это не тот тип и бросить исключение. Что меня вообще не устраивает например. Вот вызывать какой-нибудь конструктор для преобразования не выйдет для произвольного типа в аргументе. Только для ограниченного списка типов и там шаблонная магия с довольно кривой is_assignable, купой проверок и прочей гадостью.

#54
22:53, 21 фев. 2013

glap
> Вот в этом месте по хорошему и нужно динамическое приведение типа для
> аргументов.
> Если в делегате сидит double, а вызывающая сторона туда суёт int, то надо бы
> эту ситуацию нормально обработать.
> Может я чего-то не улавливаю?


динамик_каст для этого не нужнен.
Делегат нацелен на вполне определенную функцию, и знает какие у неё аргументы.

Когда вы используете функционал функтора:

connector(param);

вы запускаете делегат с аргументом некоторого типа. И делегат может проверить: является ли этот тип допустимым для вызова той функции, на которую он нацелен.

В моём коннекторе для этого используются стратегии приведения типов, которые контролируют соответствие типов, и их квалификаторов const (актуально для ссылок/указателей)

сравнение идет по типам аргументов, а не по самим аргументам.

Рассмотрим пример:

const bool result = typeid(int)==typeid(float); 

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

Для работы с делегатом, нет ни одной потребности оперировать объектами. Все операции происходят с типами. Поэтому, никакие ртти не нужны.


Единственное - если вы используете функтор, то бишь сначала:

Connector con (&Foo); //нацелились

...

con(param);  //И только потом запускаете:

То исход дела известен только времени выполнения. Проверки и передача аргументов поставщикам происходят практически мгновенно, так, словно это передача через обычный void*

Но что при этом получится - не известно.

Мой механизм примерно на 70% - это система безопасности, которая занимается только тем, что бы не допустить порчу квалификатора, или передачу некорректного типа данных.

#55
23:04, 21 фев. 2013

LuckyMan
> как я понимаю, твой Connector  проверяет типы параметров на этапе исполнения,
> по-моему, лучше, чтоб проверка была на этапе компиляции как у std::function.

Минусов у такого подхода великое множество. Все они так, или иначе вытекают из проблемы "единственного типа данных".

Мой коннектор предоставляет возможность строгих контракторв по такому же принципу, как и std::function
Есть специальная фабрика, которая создает коннектор параметризированый сигнатурой. Вот только на практике, в реальности, я ни разу так эту возможность и не использовал.

Тупо потому, что это реально не удобное говно.

А теперь реши задачу с помощью std::function: http://www.gamedev.ru/flame/forum/?id=172543

Мой коннектор прожевал её за полчаса.


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

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

Промежуточное звено ни рыба ни мясо - не востребовано.

#56
23:13, 21 фев. 2013

Не очень понимаю, я видать совсем стал плох.

Мы разбираем ситуацию когда сам тип делегата не является шаблоном. Все проверки происходит соответственно во время исполнения.

Если он нацелен на функцию, которая принимает double, а в делегат передаётся int, то как произвести преобразование?

#57
23:21, 21 фев. 2013

glap
> Не очень понимаю, я видать совсем стал плох.
> Мы разбираем ситуацию когда сам тип делегата не является шаблоном. Все проверки
> происходит соответственно во время исполнения.
>
> Если он нацелен на функцию, которая принимает double, а в делегат передаётся
> int, то как произвести преобразование?

Зависит от стратегии самого делегата. Инт приводится к доубл без потерь данных.

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

Стало быть в чем проблема? запустите удаленную функцию скормив ей доубл.


Другое дело, если вы хотите запретить любые неявные преобразования. Тогда делегат поднимет тревогу (ассерты/исключения)

#58
23:26, 21 фев. 2013

Kartonagnick
> Промежуточное звено ни рыба ни мясо - не востребовано.
твое мнение, на мой взгляд, как раз ситуации, когда нужны делегаты с динамической сигнатурой - достаточно редки

Kartonagnick
> А теперь реши задачу с помощью std::function
как нибудь в другой раз)

#59
23:27, 21 фев. 2013

А с пользовательскими типами будет работать? Вызовет нужный конструктор преобразования или оператор возврата если аргументы не совпали с параметрами?

Как это сделать если сигнатура явно не указана в коде того, кто вызывает?

Страницы: 13 4 5 617 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.