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

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

Страницы: 111 12 13 1417 Следующая »
#165
8:02, 15 апр. 2013

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

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

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

По сути, твой протокол нисколько не помогает твоему делегату построить эффективную реализацию, избежать запусков копирующих конструкторов, произвести глубокий анализ типа цели. Ну и какой тогда вообще от него прок?

+ Показать

#166
9:25, 15 апр. 2013
Kartonagnick
> У меня в жизни очень крутые перемены наступили.
Позволите нам за вас порадоваться?
#167
9:28, 15 апр. 2013

laMer007
> Позволите нам за вас порадоваться?

Конечно-конечно, как вам будет угодно
#168
9:39, 15 апр. 2013
Kartonagnick
>>> крутые перемены
> Конечно-конечно, как вам будет угодно
Что-то я не подумал, может изменения плохие... Если, конечно, это вы не про школу...
С учетом вашего сарказма что-то из этого.
Так что извиняюсь.
#169
9:57, 15 апр. 2013

laMer007
> Что-то я не подумал, может изменения плохие... Если, конечно, это вы не про
> школу...
> С учетом вашего сарказма что-то из этого.
> Так что извиняюсь.

Все хорошо. Не нужно извиняться
#170
10:41, 15 апр. 2013
Kartonagnick
> Все хорошо.
Тогда поздравляю, что бы это ни было. :)
#171
14:10, 15 апр. 2013

Мне интересно - а вот скажите, вышеотписавшиеся разработчики умных делегатов о такой вещи.
Если бы в С++ не было бы сабжа (вообще отсутствовал бы), а был бы другой механизм, вот такой:

// объявляние типа похоже на указатель на функцию
// но ключевое слово class (я его приткнул в конец, но
// не уверен что лучше было бы пихать после * или еще куда по логике языка)
// показывает что это указатель на метод
typedef void (* MethodPtr)(int) class; 

class Test1
{
public:
   void meth1( int ) ...
};

class Test2
{
public:
   void meth2( int ) ...
};

...

Test1 test1;
Test2 test2;
MethodPtr ptr; 
ptr = test1.meth1;
ptr( 1 );
ptr = test2.meth2;
ptr( 2 );
ptr имеет размер двух указателей - указатель на объект и указатель на начало метода в памяти.
В строчке
ptr = test1.meth1
выражение справа (test1.meth1) является составным типом указателя на функцию - поле "указатель на объект" автоматически инициализируется значением &test1, а поле указатель на начало метода вычисляет фактический адрес начала метода (например, важно заметить, в случае виртуального метода обращается к vtbl объекта test1, чтобы извлечь фактический адрес) ну и далее оно сразу присваивается ptr-у который, как понятно и есть этот тип - тип "указатель на метод".
Как видим
а) указатель на метод можно получить только у фактического объекта
б) он существует неразрывно с указателем на сам объект и конструируется в точке присвоения
в) выражение test.meth1( 1 ) есть частный (быстрый) случай { auto method_ptr = test.meth1; method_ptr( 1 ); }, т.е. строго соблюдается логика языка. test.meth есть callabe-объект и привычная запись obj.meth(...) есть лишь создание этого callabe объекта оператором точка и вызов его оператором скобка.
г) такой указатель на функцию не хранит в сигнатуре информацию о классе объекта - она ему вообще не нужна, поэтому пользоваться можно им по отношению к любым объектам даже не завязанным в одной иерархии наследования. главное чтобы была одинаковая сигнатура функции (с пометкой что это именно функция класса, а не просто функция).

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

#172
15:51, 15 апр. 2013

=A=L=X=,  намного удобнее в плане использования и читабельности. Только можно и не ограничиваться только методами.

typedef void (* MethodPtr)(int) class;

можно же использовать и без класса:

MethodPtr ptr; 
ptr = meth1; //цель: свободная функция
ptr(1);

Размер MethodPtr в любом случае sizeof(void*)*2;
И если ему кормить свободную функцию, компилятор может допетрить как запустить её без всяких this

В таком случае получится оч удобный человечачий делегат с протоколом

#173
16:03, 15 апр. 2013

Kartonagnick
> Размер MethodPtr в любом случае sizeof(void*)*2;
> И если ему кормить свободную функцию, компилятор может допетрить как запустить
> её без всяких this
>
> В таком случае получится оч удобный человечачий делегат с протоколом

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

#174
16:50, 15 апр. 2013

=A=L=X=
> Не вижу решения как это сделать машинноэффективно. Либо рантайм проверка при
> вызове, что я считаю очень плохо. Либо... хм... компиль может сгенерить
> трамплин который отбрасывает this и перезапускает функцию с теми же
> аргументами, но это как минимум двойной прыжок (даже вместе с хаками стека),
> тоже как то некузяво. Я честно говоря не вижу смысла такой ценой достигать
> такой цели.

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

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

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

#175
17:49, 15 апр. 2013

Kartonagnick
> Почему ты не использовал эту техническую подробность?
слишком много пришлось бы дополнительно писать.
Kartonagnick
> Я не буду критиковать реализацию, хотя она и очень очень сыроватая в плане
> эффективности и гибкости.
ясень пень, я особо и не старался расписывать.
Kartonagnick
> Ну и какой тогда вообще от него прок?
эм... я думал ты догадаешься сам для чего нужны делегаты, они нужны чтобы делегировать, а не производить глубокий анализ (хотя и можно).
Kartonagnick
> Твой делегат этот момент вообще не учитывает. Ты наверное, об этом моменте даже
> и не вспомнил.
да, забыл как-то. но это же только для теста а не идеальная реализация, идеальная реализация у меня учитывает и const (иначе бы она с лямбдами не работала бы)
Kartonagnick
> Сама библиотека имеет лицензию GPL2, и доступна по адресу:
demo_connector не компилируется. ругается на { Den::TConnector<int(int)> delegat(&Free1,10); int val = delegat();  assert(val==3); }
если закоментить то пашет. (VS2012)

struct TestString: public string
{
  TestString(const char* _Str):string(_Str){}

  operator const char*()
  {
    return c_str();
  }
};

int Test1(int i, string g, bool k) { cout << "free funtion: "<<i<<' '<<g<<' '<<k<<'\n'; return 3;}

{ Den::Connector delegat(&Test1); int val = delegat(5,"hello my little pony",true);   assert(val==3); } // падает, а не должен
{TestString lv_tmp = "trololo"; Den::Connector delegat = &Test1; int val = delegat(10, lv_tmp, true);   assert(val==3); } // падает, а тем более не должен


int Test2(int i) { cout << "free funtion: "<<i<<'\n'; return 3;}

{ Den::Connector delegat(&Test2); int val = delegat(5);   assert(val==3); } // норм
{ Den::Connector delegat(&Test2); int val = delegat(5.4);   assert(val==3); } // ошибка, что не верно, должен быть варнинг
{ Den::Connector delegat(&Test2); int val = delegat(true);   assert(val==3); } // ошибка, что не верно, каст bool к int вполне легален
{ Den::Connector delegat(&Test2); int val = delegat("some string shit");   assert(val==3); } // ошибка, но в рантайме! а должна быть на этапе компиляции и даже строку с ошибкой не найти, и почему асерт не содержить инфу?
{ Den::Connector delegat(&Test2); int val = delegat("some string shit", 5);   assert(val==3); } // ошибка, но в рантайме! а должна быть на этапе компиляции и даже строку с ошибкой не найти, и почему асерт не содержить инфу?

int Test3(char* str) { cout << "free funtion: "<<str<<'\n'; return 3;}
{ Den::Connector delegat(&Test3); int val = delegat("ololo");   assert(val==3); } // ошибка, что не верно, должен быть варнинг и то не обязательно


int Test3(const char* str) { cout << "free funtion: "<<str<<'\n'; return 3;}
{ Den::Connector delegat(&Test3); int val = delegat("ololo");   assert(val==3); }// ок
{ TestString lv_tmp = "trololo"; Den::Connector delegat(&Test3); int val = delegat(lv_tmp);   assert(val==3); }// ошибка, а не должно быть.

// c TConnector всё хорошо.
Вывод:
Шаблонный делегат работает как надо, нормальные касты, нормальные варнинги, всё на этапе компиляции.
Не шаблонный же делегат не работает так как нужно, рождает исключения не там где нужно, нет кастов, нет варнингов, исключения не содержат текст ошибки, при работе в команде эту будет кошмар.
Вот пример.
int Test1(int i, string g, bool k) { cout << "free funtion: "<<i<<' '<<g<<' '<<k<<'\n'; return 3;}
{ Den::Connector delegat(&Test1); int val = delegat(5,"hello my little pony",true);   assert(val==3); }
Всё правильно с точки зрения языка, но у тебя будет исключение, и такую ошибку будут искать вечность, а что самое печальное ты не уходишь от не знания сигнатуры, а наоборот делаешь её ещё более жёсткой из-за того что касты отсутствуют, и при этом эту сигнатуру программист должен искать в месте вызова, а не объявления + каждый вызов должен иметь строжайше выдержанную сигнатуру, пример смотри выше. То есть со всех сторон одни минусы.
Kartonagnick
ты по прежнему убеждён что твои не шаблонные делегаты, лучше шаблонных?
p.s.
ты код обфускатором что ли обрабатывал? Код жесть особенно вот эти вещи { Den::TConnector<int()>    delegat(&Free0);    int val = delegat();  assert(val==3); }
Копи паста просто ппц, можно же было заюзать макросы + #include как у меня.
И количества кода что ты написал просто охренеть, понятно что там дофига копипаста но блин, у меня реализация строчек 300.
#176
19:42, 15 апр. 2013

Kartonagnick
> Ну а ты глянь на любую реализацию существующих делегатов: в основе любой
> реализации лежит виртуальная функция, через которую осуществляется запуск либо
> метода, либо свободной функции. Делегат по самой идее своего существования
> должен уметь легко нацеливаться на любые функции/методы.

Делегат - возможно.
Указатель на метод - нет.
Если ты внимательно перечитаешь мой пост №171 то ты там нигде не увидишь слова "делегат" по отношению к предлагаемому механизму. И я это сделал намеренно. Потому что то что там предлагается пожалуй не нужно смешивать с уже довольно абстрактным и обросшим в разных ЯВУ уберфичами понятием "делегат" (например в C# делегаты помимо семантики вызова метода еще поддерживают цепные связки - что уже воще через край). Я лишь показал то как я вижу каким должен был быть в С++ на самом деле указатель на метод. Не больше но и не меньше. Чисто лишь простой и эффективный (что немаловажно - машинноэффективный - немаловажно для микроконтроллеров всяких и реалтаймосов) механизм отложенного вызова метода объекта. Он не совместим с понятием указателя на функцию и ему на самом деле это не надо.
Там где хочется каких то лютых бешенных абстракций - С++ позволяет городить свои уберклассы, аки std:function и мирится с платой скорости за универсальность. Это нормально.

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

#177
20:13, 15 апр. 2013

На самом деле что требуется от указателей на метод? Да ровно того же и только того же что и для указателей на функции - отложенно вызвать код.
Совершенно естественно что для отложенного вызова функции нужно знать а) её сигнатуру б) её адрес, всё
Так же естественно что для отложенного вызова метода нужно знать а) его сигнатуру в) адрес метода г) адрес объекта (this).
Как я выше написал сия идиома языка должна была по идее проектироваться полностью линейно и пряморуко:
Если для функции чей вызов выглядит как func(...) нужно взять её callable-сущность (технически - просто адрес), то мы пишем func (без скобок) и имеем его на руках. Применяя к нему оператор скобка - совершаем вызов. Т.е. func(...) это есть не что иное как быстрая запись получения callable-сущности и тут же применение к нему скобок - т.е. вызова.
Точно так же логично было бы сделать что если вызов метода выглядит как obj.meth(...) то его callable-сущность это obj.meth - в ней должна быть зашита вся информация нужная для вызова потом через оператор скобку - и это действительно реально сделать по месту сего выражения. Т.е. оператор точка применительно к объекту, если справа стоит имя метода должен как раз возвращать callable сущность в которой зашит а) сам объект б) сигнатура вызова (неявно предполагается наличие this, в остальном это то же самое что и сигнатура функции), б) адрес метода в памяти. Всем этим действительно оператор точка в данной записи обладает и вызов obj.meth(...) всё точно так же является а) получением callable сущности оператором точка и б) вызовом её оператором скобка
Логично что obj.meth и есть то что должно было бы в языке называться указателем на метод. Его внутренняя структура тут очевидна - это просто два указателя - this и адрес метода. Причём в отличие от указателей на функции в текущей реализации языка - указатель на метод уже полностью вычислен в операторе точка (т.е., к примеру, вычислен адрес по vtbl для виртуального метода - этим оператор точка и занимается) и остаётся только вставить ассемблерные инструкции push this; ... ; call meth;
Таким образом сиё решение не просто кратко лаконично и позволяет решить саму задачу, но еще и абсолютно машинноэффективно - эффективней и не придумаешь собственно, а это для C++ всегда был идеал и флаг на который держался курс. Причём самый профит решения - в том что сигнатура callable-сущности полностью избавлена от знания какой класс у объекта - ей это совершенно не надо - действительно - для абсолютно любого класса инструкции будут одни и те же - push this; ... call meth; Это позволяет их с ходу в карьер использовать как полноценные кэллбеки в ООП-е по аналогии с делегатами в других языках.
Те же уродливые указатели на функции что есть в языке сейчас мало того что неудобны в использовании, но еще и не могут быть полноценно использованы по прямому назначению - по организации нормальных callback-ов - из-за того что у них в сигнатуре жестко прописан класс и даже более того - этот класс нельзя заменить даже его наследником! Т.е. сущность сего решения обрубает осмысленные варианты применения его как callback-а, оставляя только в роли костыля для шаблонной магии. К тому же оно еще и машиннонеэффективно даже (в роли кэллбеков) т.к. постоянно вычисляет адрес виртуального метода снова и заново.
Причём специфика применения этих Страуструповских указателей на методы - т.е. та задача где они реально могли бы быть полезны - например опрос коллекции объектов на вызов одного и того же но заранее в рантайме не известного метода - это жесть как ненужная в реальности задача. Хотя теоретически она может даже в крайне редких случаях быть полезна, так что тот механизм я бы вполне оставил бы даже. Но нормальные указатели на функции на мой взгляд нужны и полезны много больше.

P.S.

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

#178
14:19, 16 апр. 2013

Kartonagnick
сегодня покопался немного и оказалось что у тебя ппц как всё сильно типизировано, в то время как у меня допускается даже такое

int Ololo(float i, string j, int k)
{
   cout<<i<<' '<<j<<' '<<k<<'\n';
   return 1;
}

Delegate<void (int,const char*,bool)> lv_Delegate = &Ololo;
#179
18:31, 16 апр. 2013

Гм.. Какой сегодня год на дворе?

        shared_ptr<Rabbit> rabbit=make_shared<Rabbit>();
        Den::Connector delegat(rabbit, &Rabbit::Method0);//?

И немного об быстродействии:

+ Показать

Kartonagnick
> избежать запусков копирующих конструкторов
Гм...
+ Показать

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

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