Функциональные типы (параллельная ветка)
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.06.05 18:46
Оценка: 1 (1) +1 :)
Здравствуйте, eao197, Вы писали:

VD>>Нет? И я могу вот так за просто получить в С++ указатель на произвольный метод?


E>Дело в том, что

E>1) в C++ смысла от этого ноль -- ведь чтобы вызвать метод по указателю, нужно знать точную сигнатуру метода.

Похоже ты не понимащь о чем я веду речь.

E>2) с помощь явных кастов в C++ можно привести указатель на один метод к указателю на другой метод. Только это нафиг не нужно.


Это тут не причем.

VD>>Странно. А ведь boost::bind это море очень не быстро компилируемого кода который использует уж совсем не кчемную функциональность языка вроде указателей на методы. И все это вмесо примитивной модификации языка.


E>Никчемность указателей на методы -- это собственное впечатление?

E>Лично мне они кажутся настолько же удобными, как и указатели на функии. И время от времени я ими пользуюсь.

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

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

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


E>Опиши.


Может лучше поискать?

Ладно. Попробую...

Итак в старые добрые времена когда ООП был только в речах продвинутых ученых и Смолтоке в С был очень удобный тип — указаетель на функцию. Мы могли взять имя функции и передаь его куда угодно, чтобы в последсвии этот кто угодно вызвал нужную нам функцию. Даже оконная функция в виндовс — это указатель на функцию. Если нам было нужно передать контекст, то мы просто передавали два указателя один на функцию, а второй на переменную олицетварющую контекст. И все было зашибись, до тех пор пока на сменую С не пришел С++.

В этот момент уже существовало не мало языков в которых был функциональный тип. Дже в Смолток было нечто вроде. Страуструп почему-то решил, что делать указатель на метод с определенной сигнатурой но не привязанный к конкретному классу — это не объектно ориентированно, не безопастно и вообще мовитон. О том что людям как не крути потребуется еще и указатель на объект он вообще думать не захотел. Но сделать объекто ориентированный аналог указателя на функцию из С он все же захотел. В результате родился уродец под названием указатель на метод-член конкретного класса. Это указатель не хранит ссылку на объект, но зато очень жестко привязан к классу в котором должен быть метод на ктороый нужно получить указатель. В итоге получается, что через него можно вызвать только методы конкретного класса. Но для ОО-колбэков и программирования в функциональном стиле это и на фиг не уперлось! Для них нужна возможность вызвать метод у объекта тип которого не важен для вызывающей стороны. В общем получился уродец единственно существенное применение которого стала эмуляция делегатов. При этом получается целая гора кода которую не только долго компилировать, но еще и чертовски сделать универсальной (компилирующейся на разных компиляторах). На сегодня появились реализации вроде boost::bind, но это только сеогодня и компилируются они только на правильных компиляторах. Так что из-за ошибки Страусрупа и откровенно детского каприза много лет народ мучился. Конечно библиотека — это лучше чем ничего. Но это эмуляция примитива горой кода! И зачем капризничать вместо того чтобы ввести еще один примитив лично я не понимаю.

E>


Какое отношение этот рассказ имеет к вопросу? Повторю еще раз цитаты:
VD>>>Да, не понимаю. Сборка это набор байт. Всегда можно скачать...

E>>Нет. Далеко не всегда.


VD>И когда нельзя?

VD>Раскрывай.
E>

E>Use case 2: Unknown Traits

Хотлось бы услышать ответ на мой вопрос, а не монолог на отвлечанную тему.

Что же до сериализации, то это проблемы С++ в ремоутинге и КОМ-е они решаются.

E>

E>Use case 3: Protocol Versions

E>Так же описано: Неизвестные расширения (можно прочитать пункты Введение и Применение неизвестных расширений)


К сожалению я пишу в офлайне и тыкать по внешним ссылкам не могу. Так что ты лучше кратенько приведи обоснование почему нельзя передать сборку на клиента? Причем не нужно рассказывать по тараканы плюсов или еще какой-то технологии. Мы же с дотнетом сравнивали. Вот и ошиши причины невозможности в отношении сборок дотнета.

E>


E>Хочу отметить, что все приведенные случаи не требуют того, чтобы взаимодействующие стороны точно знали типы используемых для коммуникации объектов.


Для передачи по сети нужна только сериализация. Если она делается вручную или генератором кода, то на клиенте сборка не обязательна. Ну, и всегда можно сделать простенькое решение которое сможет копировать нужные интерфесные сборки в автомате. Итго — это не языковая проблема. Это проблема дизайна.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>

29.06.05 12:09: Ветка выделена из темы Версии интерфейсов
Автор: eao197
Дата: 21.06.05
— AndrewVK
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[16]: Функциональные типы (параллельная ветка)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 25.06.05 21:54
Оценка: 4 (1) +2
Здравствуйте, VladD2, Вы писали:

VD>>>Странно. А ведь boost::bind это море очень не быстро компилируемого кода который использует уж совсем не кчемную функциональность языка вроде указателей на методы. И все это вмесо примитивной модификации языка.


E>>Никчемность указателей на методы -- это собственное впечатление?

E>>Лично мне они кажутся настолько же удобными, как и указатели на функии. И время от времени я ими пользуюсь.

VD>Приведи пример где ты их используешь. Я видел их использование только в коде который пытался эмулировать функцинальность делегата (назавем его так, чтобы отличать от того что есть в С++).


VD>Только перед тем как приводить убедись, что ты правильно меня понял. Речь идет не о С-шном указателе на глобальную функцию, а о С++-ном указателе на функцию-член класса.


Я понял, что ты говоришь про указатели на методы класса.

Один из use case в которых я использую указатели на методы -- это реализация в объекте-классе чего-то типа конечного автомата. Наружу такой объект предоставляет, например, один метод: receive_event( event_t event ), а внутри себя должен выполнить действия в зависимости от состояния объекта. Решение в лоб выглядело бы так:
void some_class_t::receive_event( event_t event )
    {
        if( state_one == current_state() )
            {
                if( event_one == event )
                    do_something();
                else if( event_two == event )
                    switch_state( state_two );
                ...
            }
        else if( state_two == current_state() )
            { ... }
        ...
    }

А если применить указатели на методы:
void some_class_t::receive_event( event_t event )
    {
        (this->*m_state_handler)( event );
    }
void some_class_t::state_one_handler( event_t event )
    {
        if( event_one == event )
            do_something();
        else if( event_two == event )
            // Для переключения состояния просто меняем указатель на текущий обработчик.
            m_state_handler = &some_class_t::state_two_handler;
    }

А можно было бы пойти еще дальше. Скажем завести отдельный map< event_t, (some_class_t::*)() > для каждого состояния. И занести в эти map-ы указатели на методы, которые должны вызываться в конкретном состоянии по конкретному событию. Тогда смена состояния будет означать просто смену указателя на нужный map. А работа метода receive_event -- поиск указателя в map-е по идентификатору события и вызов найденого обработчика.

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


E>>Опиши.


VD>Ладно. Попробую...


<...Эмоциональное описание того, что тебе кажется неправильным поскипано...>

Твоя точка зрения понятна. Но, имхо, она не правильная, а я согласен со Страуструпом.

E>>


VD>Какое отношение этот рассказ имеет к вопросу? Повторю еще раз цитаты:

VD>>>>Да, не понимаю. Сборка это набор байт. Всегда можно скачать...

E>>>Нет. Далеко не всегда.


VD>>И когда нельзя?

VD>>Раскрывай.

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

VD>Что же до сериализации, то это проблемы С++ в ремоутинге и КОМ-е они решаются.


Насколько я знаю, там все решается на уровне жестко определенных интерфейсов. Причем не только в КОМ-е, но и в Corba. Как только интерфейсы перестают удовлетворять потребностям приходится вводить новые интерфейсы. Затем новые. Затем опять новые и т.д.

А вот в Asn1 было введено понятие "extension points" -- это такие места в спецификации протокола, где говорится -- вот в этом месте в будущем может что-то появиться. Что именно, пока никто не знает. Но если появится, то все, что здесь будет, может быть проигнорировано. Благодоря таким extension points обе стороны смогут обмениваться данными даже если их версии протоколов сильно отличаются.

Вот я и спрашивал, поддерживает ли BinaryFormatter в .Net подобные фишки. И все мои примеры как раз были об этом.

VD>К сожалению я пишу в офлайне и тыкать по внешним ссылкам не могу.


Понятно. Только, если тебе интересно, то выбери время и сходи. А нет, так я не вижу смысла копипастить сюда несколько полтора десятка килобайт описаний.

VD> Так что ты лучше кратенько приведи обоснование почему нельзя передать сборку на клиента?


Потому что конкретный прикладной протокол придется заморачивать не только на передачу прикладных данных, но и на передачу кода.
Кроме того, если речь идет о банковских системах, то на 100% могу утверждать, что пробить через безопасность банка решение о динамической подгрузке на банковский сервер извне какого-то кода будет архисложно.

E>>Хочу отметить, что все приведенные случаи не требуют того, чтобы взаимодействующие стороны точно знали типы используемых для коммуникации объектов.


VD>Для передачи по сети нужна только сериализация. Если она делается вручную или генератором кода, то на клиенте сборка не обязательна.


Вот именно. Но я говорю о случаях, когда сериализация/десериализация на разных сторонах делается по разным версиям описания схемы данных.

VD> Ну, и всегда можно сделать простенькое решение которое сможет копировать нужные интерфесные сборки в автомате. Итго — это не языковая проблема. Это проблема дизайна.


Именно дизайна. И попытаться решить нетривиальную проблему дизайна простенькими средствами языка может быть слишком сложно.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[16]: Функциональные типы (параллельная ветка)
От: Павел Кузнецов  
Дата: 26.06.05 14:25
Оценка: 1 (1) +2
Здравствуйте, VladD2, Вы писали:

VD> нужна возможность вызвать метод у объекта тип которого не важен для вызывающей стороны. В общем получился уродец единственно существенное применение которого стала эмуляция делегатов. При этом получается целая гора кода которую не только долго компилировать, но еще и чертовски сделать универсальной (компилирующейся на разных компиляторах). На сегодня появились реализации вроде boost::bind, но это только сеогодня и компилируются они только на правильных компиляторах.


1) Все, кому было надо, имели свои варианты boost::bind уже давным-давно. В простом виде это было сделано в незапамятные времена в виде std::bind1st и std::bind2nd. Кому надо -- обобщали.

2) По поводу "правильных компиляторов": если посмотреть на результаты тестов, то можно увидеть, что bind в объеме делегатов C# работает на всех известных компиляторах.

Собственно, в том же C# уже потихоньку появляются те же проблемы с совместимостью разных версий компиляторов: часть новых возможностей доступна только в новом компиляторе, у пользователей старых версий компилятора этих возможностей нет. Это совершенно естественное явление, сопутствующее развитию языка при наличии пользователей предыдущих версий.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[16]: Функциональные типы (параллельная ветка)
От: vdimas Россия  
Дата: 27.06.05 18:09
Оценка: 30 (2) +2 -1
Здравствуйте, VladD2, Вы писали:


VD>При этом получается целая гора кода которую не только долго компилировать, но еще и чертовски сделать универсальной (компилирующейся на разных компиляторах). На сегодня появились реализации вроде boost::bind, но это только сеогодня и компилируются они только на правильных компиляторах. Так что из-за ошибки Страусрупа и откровенно детского каприза много лет народ мучился. Конечно библиотека — это лучше чем ничего. Но это эмуляция примитива горой кода!


Знаешь, в чем неверный поинт. В том, что этого примитива нет и в дотнете. Там есть вполне реальный экземпляр вполне реального класса — делегата с конкретным типом. В С++ непринято создавать объекты "за ширмой", сама философия языка такая, что ты полностью контроллируешь процесс описания классов и создания их экземпляров (и уж тем более, ничего "само" не генерится в run-time). И создание делегата в т.ч.

Вот тебе простая суть решения и я не вижу горы кода:

Объяви пару структур:
template<typename FuncT>
struct Functor;

template<typename ObjT, typename FuncT>
struct Delegate;

введи несколько специализаций:
template<> struct Functor<void()> { virtual void operator()()=0; };

template<typename ObjT> 
struct Delegate<ObjT, void()> : Functor<void()>{
    Delegate(ObjT* obj_, (ObjT::*func_)()) : obj(obj_), func(func_) {}

    virtual void operator()() { (obj->*func)(); }
protected:
    ObjT*   obj;
    MemberFuncT func;
};

template<> struct Functor<typename ParamT1> { virtual void operator()(ParamT1)=0; };

template<typename ObjT, typename ParamT1> 
struct Delegate<ObjT, void(ParamT1)> : Functor<void(ParamT1)> {
    Delegate(ObjT* obj_, (ObjT::*func_)(ParamT1)) : obj(obj_), func(func_) {}

    virtual void operator()(ParamT1 p1) { (obj->*func)(p1); }
protected:
    ObjT*   obj;
    MemberFuncT func;
};

// и т.д.

и используй:

typedef void MyDelegate(char *s); // почти как в C# :)

struct MyStruct {
    Functor<MyDelegate>* callback;
    void Fire() { (*callback)("Hello!"); }
};

class MyHandler {
[...]

  void Method1() {
     MyStruct s1;
     s1.callback = new Delegate<MyHandler, MyDelegate>(this, &HandlerMethod);
     s1.Fire();
  }

  void HandlerMethod(char* s) { std::cout << s; }

};


Вот полная версия http://www.rsdn.ru/Forum/Message.aspx?mid=908731&amp;only=1
Автор: vdimas
Дата: 21.11.04

Пользоваться ей еще проще, чем в приведенном примере.

ВСЕ!!! Пишется за 15 мин и потом всю жизнь используется. Более того, могу наследовать, добавлять поведение и пр. и пр. и пр. (boost::bind — это заготовки вообще на все случаи жизни)

В общем, Страуструп просто напросто не стал ограничивать возможности разработчика автоматически "за кулисами" генеренным классом.

Ты еще, кстати, забыл про контроль над временем жизни. Делегат будет держать произвольное время указуемый объект в дотнете. В С++ тебе предоставляется произвольные возможности по контолю за временем жизни, компилятор не в курсе, что именно ты используешь. (есть программы, которые обходятся вообще без new, использующие лишь глобальные и локальные переменные — например втраиваемое ПО. При таком раскладе все еще проще).

В общем, твой поинт немного неккоректен по отношению к целям и задачам С++, и более того — технически нереализуем.
Re[17]: Функциональные типы (параллельная ветка)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.06.05 21:30
Оценка: +1 :))
Здравствуйте, vdimas, Вы писали:

V>Знаешь, в чем неверный поинт. В том, что этого примитива нет и в дотнете. Там есть вполне реальный экземпляр вполне реального класса — делегата с конкретным типом. В С++ непринято создавать объекты "за ширмой", сама философия языка такая, что ты полностью контроллируешь процесс описания классов и создания их экземпляров (и уж тем более, ничего "само" не генерится в run-time). И создание делегата в т.ч.


V>Вот тебе простая суть решения и я не вижу горы кода:

К сожалению, во-первых простая суть решения — это капля в море. Как только ты начнешь поддерживать различные сигнатуры делегатов, а также комбинирование в multicast делегаты, то тебе сразу станет поскушнее.
Во-вторых,
V>В общем, Страуструп просто напросто не стал ограничивать возможности разработчика автоматически "за кулисами" генеренным классом.
Да-да. Поэтому разработчики, так классно неограниченные Страуструпом, должны были сначала дождаться введения шаблонов в С++, а потом сидеть и куковать до XXI века, пока компиляторы не научились делать частичную специализацию. В каком году вышел VC++ 7.0? Предыдущую версию компилятора ты небрежно оставил за бортом.

V>В общем, твой поинт немного неккоректен по отношению к целям и задачам С++, и более того — технически нереализуем.

В общем, твой поинт немного некорректен. См., напр. здесь
Автор(ы): Александр Шаргин
Дата: 19.03.2003
Делегаты в CLR удобны, типобезопасны и эффективны. Последнее время на форумах RSDN часто поднимается вопрос о том, можно ли реализовать делегаты с аналогичными свойствами, оставаясь в рамках "чистого" C++. Оказывается, это вполне возможно. В этой статье я покажу, как это сделать.
. Вот он, кросс-платформенный делегат. При помощи шабонов, макросов, копи-пейста и такой-то матери. Я, конечно, далеко не гуру С++. Но написать такое за 15 минут я бы не смог.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Функциональные типы (параллельная ветка)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 27.06.05 21:58
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

V>>В общем, Страуструп просто напросто не стал ограничивать возможности разработчика автоматически "за кулисами" генеренным классом.

S>Да-да. Поэтому разработчики, так классно неограниченные Страуструпом, должны были сначала дождаться введения шаблонов в С++, а потом сидеть и куковать до XXI века, пока компиляторы не научились делать частичную специализацию. В каком году вышел VC++ 7.0? Предыдущую версию компилятора ты небрежно оставил за бортом.

У C++, к счастью, есть компиляторы и кроме Visual C++.
Кстати, официального релиза generic-ов в Java и C# разработчикам пришлось ждать и после наступления XXI века.

V>>В общем, твой поинт немного неккоректен по отношению к целям и задачам С++, и более того — технически нереализуем.

S>В общем, твой поинт немного некорректен. См., напр. здесь
Автор(ы): Александр Шаргин
Дата: 19.03.2003
Делегаты в CLR удобны, типобезопасны и эффективны. Последнее время на форумах RSDN часто поднимается вопрос о том, можно ли реализовать делегаты с аналогичными свойствами, оставаясь в рамках "чистого" C++. Оказывается, это вполне возможно. В этой статье я покажу, как это сделать.
. Вот он, кросс-платформенный делегат. При помощи шабонов, макросов, копи-пейста и такой-то матери. Я, конечно, далеко не гуру С++. Но написать такое за 15 минут я бы не смог.


А ты бы смог сделать делегаты, создавая компилятор C#?
Я это к тому, что всем должны заниматься профессионалы в своей области. Ведь boost в C++ пишут очень продвинутые люди.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[17]: Функциональные типы (параллельная ветка)
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.06.05 22:21
Оценка: -3
Здравствуйте, eao197, Вы писали:

E>Я понял, что ты говоришь про указатели на методы класса.


Это радует.

E>Один из use case в которых я использую указатели на методы -- это реализация в объекте-классе чего-то типа конечного автомата. Наружу такой объект предоставляет, например, один метод: receive_event( event_t event ), а внутри себя должен выполнить действия в зависимости от состояния объекта. Решение в лоб выглядело бы так:


Знашь, я заметил за тобой одну проблему которая все время убивает дискуссию. Ты излишне переусложняешь свои примеры.

Какие-то конечные автоматы... Надо выделить основную сущьность и показать ее суть, а не наворачивать огромные примеры в которых сложно разобраться (а точнее очень не хочется разбираться).

Твой пример изумительно решается как на на обычных указателях на глобальны функции (не методы класса), так и на делегатах. Вот реализация на делегатах:
delegate void EventHandler(event_t event);
delegate void MyEventHandler(int eventId);

class some_class
{
    public some_class() { _eventHandler = state_one_handler; }
    
    MyEventHandler _eventHandler;

    void receive_event(int eventId)
    {
        _eventHandler(eventId);
    }

    void state_one_handler(int eventId)
    {
        if (eventId == 1)
                do_something();
        else if (eventId == 2)
                // Для переключения состояния просто меняем указатель на текущий обработчик.
                _eventHandler = state_two_handler;
    }

    void state_two_handler(int eventId)
    {
        // ...
    }

    void do_something() { }
}


Основная суть здесь использование указателя на метод в качестве ссылки на алгоритм.

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

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

Все что нужно сделать, это убрать этот лишний контроль. Но теперь уже в дело вступили амбиции. Убрать это ограничение значит признать собственную ошибку. А вот это-то похоже Страуструп делать очень не хочет. Не мог же он ошибиться в дизайне?

E>А можно было бы пойти еще дальше. Скажем завести отдельный map< event_t, (some_class_t::*)() > для каждого состояния. И занести в эти map-ы указатели на методы, которые должны вызываться в конкретном состоянии по конкретному событию. Тогда смена состояния будет означать просто смену указателя на нужный map. А работа метода receive_event -- поиск указателя в map-е по идентификатору события и вызов найденого обработчика.


Ага. Очень хороший прием. И в Шарпе он используется на право и на лево. Причем выглядит очень компактно и красиво. В сочетании с рефлекшоном он позволяет просто таки творить чудеса. Вот, например, простенький пример крэкера сообщений написанного на C# Крекер Windows-сообщений в AOP-стиле
Автор: VladD2
Дата: 11.05.04
. Благодаря мапу делегатов, рефлекшону и атрибутам обработку сообщений виндовс получилось сделать декларативным:
// Костанты-идентификаторы сообщений. Можно надыбать на www.codeproject.com или в winuser.h
const int WM_LBUTTONDOWN   =  0x0201;
const int WM_RBUTTONDOWN   =  0x0204;

// Обработчики сообщений. Атрибутом WinMsg с ними ассоциируются сами сообщения.

[WinMsg(WM_RBUTTONDOWN)]
bool OnRButtonDown(ref Message msg)
{
        BackColor = Color.Blue;
        return true;
}

[WinMsg(WM_LBUTTONDOWN)]
bool OnLButtonDown(ref Message msg)
{
        BackColor = Color.Red;
        return true;
}


А вот пример из редактора кода. Он довольно длинный, так что сначало опишу его применение, а потом приведу код.

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

Итак, в контроле нужно добавить следующий код:
/// <summary>
/// Ассоциативный массив ключем котрого является клавиатурное сокращения,
/// а значением обработчик выполняемый при нажатии этого клавиатурного 
/// сокращения.
/// </summary>
private KeyboardShortcutsMap _keyboardMap;

...
// В конструкторе:
_keyboardMap = new KeyboardShortcutsMap(this);

...

    protected override void OnKeyDown(KeyEventArgs e)
    {
        if (e.KeyValue >= 16 && e.KeyValue <= 18) // Alt, Control, Shift
            return;

        // Получаем обработчик ассоциирванный с текущим клавиатурным сокращением.
        KeyboardShortcutsMap.KeyHandler keyDownHandler = _keyboardMap[e.KeyData];

        if (keyDownHandler != null)
        {
            keyDownHandler();
            e.Handled = true;
            e.SuppressKeyPress = true;
        }
        else
            base.OnKeyDown(e);
    }


А вот так выглядит конфигурационный файл:
<?xml version="1.0" encoding="utf-8" ?>
<Shortcuts>
    <!-- Навигация по тексту -->
    <Shortcut Key="Up"                      Action="CaretVirtualLineUp"/>
    <Shortcut Key="Down"                    Action="CaretVirtualLineDown"/>
    <Shortcut Key="Shift | Up"              Action="CaretVirtualLineUpExtend"/>
    <Shortcut Key="Shift | Down"            Action="CaretVirtualLineDownExtend"/>
    <Shortcut Key="Home"                    Action="CaretVirtualLineHome"/>
    <Shortcut Key="End"                     Action="CaretVirtualLineEnd"/>
    <Shortcut Key="Shift | Home"            Action="CaretVirtualLineHomeExtend"/>
    <Shortcut Key="Shift | End"             Action="CaretVirtualLineEndExtend"/>
    <Shortcut Key="Left"                    Action="CaretLeft"/>
    <Shortcut Key="Control | Left"          Action="CaretWordLeft"/>
    <Shortcut Key="Control | Right"         Action="CaretWordRight"/>
    <Shortcut Key="Shift | Control | Left"  Action="CaretWordLeftExtend"/>
    ...
    <!-- Клипборд -->
  <Shortcut Key="Control | C"             Action="Copy"/>
  <Shortcut Key="Control | Insert"        Action="Copy"/>
    <Shortcut Key="Control | V"             Action="Paste"/>
    <Shortcut Key="Shift | Insert"          Action="Paste"/>
    <Shortcut Key="Control | X"             Action="Cut"/>
    <Shortcut Key="Shift | Delete"          Action="Cut"/>
    ...
</Shortcuts>


А вот так выглядит реализация:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;
using System.Reflection;

namespace Rsdn.Editor
{
    /// <summary>
    /// Хэлпер-класс считывающий информацию о клавиатурных сокращениях из 
    /// файла настроек или ресурсов и преобразующий его во внутренний словарь.
    /// После инициализации можно пользоваться индексером объекта чтобы
    /// получать делегаты-обработчики клавиатурных событий.
    /// Файл должен называться KeyboardShortcutsMap.xml.
    /// Файл с описанием клавиатурных сокращений может находиться
    /// в каталоге %APPDATA%\Rsdn.Editor иди каталоге где располагаться сборка
    /// в котророй находится элемент управления.
    /// Если файл не найден в одном из этих каталогов, то описание 
    /// клавиатурных сокращений берется из ресурсов.
    /// </summary>
    internal class KeyboardShortcutsMap
    {
        /// <summary>
        /// Тип метода вызываемого при клавиатурном сокращении.
        /// </summary>
        public delegate void KeyHandler();

        const string MapFileName = "KeyboardShortcutsMap.xml";

        public KeyboardShortcutsMap(Control view)
        {
            using (XmlReader reader = OpenReader())
            {
                if (!reader.Read() || reader.Name != "xml")
                    throw new ApplicationException("Неверный формат файла KeyboardShortcutsMap.xml");

                while (reader.Read() && string.IsNullOrEmpty(reader.Name))
                    ;

                if (reader.Name != "Shortcuts")
                    throw new ApplicationException("Неверный формат файла KeyboardShortcutsMap.xml");

                Type keyHandlerType = typeof(KeyHandler);
                object[] args = new object[0];
                StringBuilder errors = new StringBuilder(); // сообщения об ошибках

                while (reader.Read())
                {
                    if (reader.Name == "Shortcut")
                    {
                        string key = null;
                        string action = null;

                        try
                        {
                            key = reader.GetAttribute("Key"); // читаем ключь
                            // читаем имя делегата
                            action = reader.GetAttribute("Action");

                            // Получам клавиатурное сокращение плюс делегат и инициализируем
                            // этими значениями внутренний словарь.
                            SetKeyboardHandler(ParseKeys(key),
                                (KeyHandler)KeyHandler.CreateDelegate(
                                keyHandlerType, view, action, false, true));
                        }
                        catch (Exception ex)
                        {
                            errors.AppendLine("Can't add key '" + key + "' with action '"
                                + action + "'. " + ex.Message);
                        }
                    }
                }

                if (errors.Length > 0)
                    MessageBox.Show(view.FindForm(), 
                        "При считывании файла с клавиатурными сокращениями были обнаружены "
                        + "следующие ошибки: " + Environment.NewLine + errors, Utils.AppName,
                        MessageBoxButtons.OK, MessageBoxIcon.Warning); 
            }
        }

        /// <summary>
        /// Открывает XML-файл содержащий описание клавиатурных сокращений
        /// на чтение.
        /// </summary>
        private static XmlReader OpenReader()
        {
            XmlReader reader;

            string path = Environment.GetFolderPath(
                Environment.SpecialFolder.ApplicationData);

            path = Path.Combine(Path.Combine(path, Utils.AppName), MapFileName);

            // Если файл с клавиатурными настройками существует в папке "Application Data" 
            // (%APPDATA%), или в папке где расположена DLL-а, то читаем настройки из него.
            // Иначе чиатаем настройки из файла сохраненного при компиляции в ресурсах.
            if (File.Exists(path))
                reader = XmlReader.Create(path);
            else if (File.Exists(path = Path.Combine(Utils.GetModulePath(), MapFileName)))
                reader = XmlReader.Create(path);
            else
                reader = XmlReader.Create(new StringReader(
                    Properties.Resources.KeyboardShortcutsMap));
            return reader;
        }

        /// <summary>
        /// Преобразует строку содержащую имена клавишь в Keys.
        /// Имена клавишь могут объеденяться по или (знаком "|").
        /// </summary>
        /// <example>"Shift | Control | Right"</example>
        /// <param name="key">Клавиатурное сокращение в виде строки.</param>
        private static Keys ParseKeys(string key)
        {
            Keys keys;
            // Разбиваем ключь на отдельные значения
            string[] keyStrs = key.Split('|');
            keys = 0;

            foreach (string value in keyStrs)
                keys |= (Keys)Enum.Parse(typeof(Keys), value);
            return keys;
        }

        /// <summary>
        /// Возвращает делегат-обработчик события соотвествующий клавиатурному
        /// сокращению или null.
        /// </summary>
        /// <param name="shortcut">Клавиатурное сокращение.</param>
        /// <returns>Делегат обработчки.</returns>
        public KeyHandler this[Keys shortcut]
        {
            get
            {
                KeyHandler keyHandler;

                if (_kbdMap.TryGetValue(shortcut, out keyHandler))
                    return keyHandler;

                return null;
            }
        }

        /// <summary>
        /// Вспомогательный метод позволяющий пуростить инициализацию _kbdMap.
        /// Ассоциирует клавиатурное сокращение и его обработчик.
        /// </summary>
        /// <param name="keyData">Клавиатурное сокращение.</param>
        /// <param name="keyHandler">Обработчик.</param>
        private void SetKeyboardHandler(Keys keyData, KeyHandler keyHandler)
        {
            if (_kbdMap.ContainsKey(keyData))
                Trace.WriteLine("The keyboard shortcut '" + keyData + "' be found twice.");

            _kbdMap[keyData] = keyHandler;
        }

        /// <summary>
        /// Ассоциативный массив ключем котрого является клавиатурное сокращения,
        /// а значением обработчик выполняемый при нажатии этого клавиатурного 
        /// сокращения.
        /// </summary>
        private Dictionary<Keys, KeyHandler> _kbdMap = 
            new Dictionary<Keys, KeyHandler>();
    }
}



Теперь зададимся впоросом, в чем главное отличие этих примеров от твоего (если не смотреть на применение рефлекшона и других компонентных технологий)? Так вот главное отличие, что класс с реализацией фичи, и класс предоставляющий список функций — это два разных класса! Вот этого и не позволяют сделать указатели на методы класса в С++. А именно это применение самое желанное.

Так что указатели на методы класса в С++ — это всего лишь досадное недоразумение. Создание частного случая вместо реализации общего.

Ну, а все эти сигналы, бинды, фаншоны из С++ не боллее чем попытка обойти достадные ограничения за счет возможностей метапрограммирования и нехилых изысков.

E><...Эмоциональное описание того, что тебе кажется неправильным поскипано...>


E>Твоя точка зрения понятна. Но, имхо, она не правильная, а я согласен со Страуструпом.


Жаль, что не смог убедить. Но спасибо хотя бы за то, что я не услышал от тебя слов вроде тех что рядом излил ПК.


VD>>Какое отношение этот рассказ имеет к вопросу? Повторю еще раз цитаты:

VD>>>>>Да, не понимаю. Сборка это набор байт. Всегда можно скачать...

E>>>>Нет. Далеко не всегда.


VD>>>И когда нельзя?

VD>>>Раскрывай.

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


Ясно. Прямого ответа на вопрос я не получу. Ну, да понятно почему...

VD>>Что же до сериализации, то это проблемы С++ в ремоутинге и КОМ-е они решаются.


E>Насколько я знаю, там все решается на уровне жестко определенных интерфейсов. Причем не только в КОМ-е, но и в Corba. Как только интерфейсы перестают удовлетворять потребностям приходится вводить новые интерфейсы. Затем новые. Затем опять новые и т.д.


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

E>А вот в Asn1 было введено понятие "extension points" -- это такие места в спецификации протокола, где говорится -- вот в этом месте в будущем может что-то появиться.


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

E>Вот я и спрашивал, поддерживает ли BinaryFormatter в .Net подобные фишки. И все мои примеры как раз были об этом.


У тебя есть некоторое представление о том, ка должен быть реализован BinaryFormatter если для его реализации использовать С++ и привычные тебе идиомы/паттерны. Но это не вереный подход. Для понимания того что такое BinaryFormatter и как он устроен нужно понять идиомы и паттерны дотнета и отказаться от взгляда на мир с точки зрения С++.

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

E>Понятно. Только, если тебе интересно, то выбери время и сходи. А нет, так я не вижу смысла копипастить сюда несколько полтора десятка килобайт описаний.


Если скопировать только суть, то смысл очень даже есть.

E>Потому что конкретный прикладной протокол придется заморачивать не только на передачу прикладных данных, но и на передачу кода.


Это можно сделать введя очень простенький компонент. В его задачи будет входить контроль наличия сборки (что в дотнете делается простым подключением на специальное событие) и подкачку нужной сборки если сборки еще нет.

Безусловно это дополнительный код, но к протоколу он отношения не имеет. Ну, и не так уж он и сложен.

E>Кроме того, если речь идет о банковских системах, то на 100% могу утверждать, что пробить через безопасность банка решение о динамической подгрузке на банковский сервер извне какого-то кода будет архисложно.


Это если люди отвечающие за решение совсем дремучи. Все проблемы безопасности решаются. Копировать можно только публичные интерфейсы.

E>Вот именно. Но я говорю о случаях, когда сериализация/десериализация на разных сторонах делается по разным версиям описания схемы данных.


Звучит не разумно.

VD>> Ну, и всегда можно сделать простенькое решение которое сможет копировать нужные интерфесные сборки в автомате. Итго — это не языковая проблема. Это проблема дизайна.


E>Именно дизайна. И попытаться решить нетривиальную проблему дизайна простенькими средствами языка может быть слишком сложно.


Ты погряз в догмах и проблемах С++. То что ты называшь "нетривиальной проблемой дизайна" на самом деле является обычной пробемой дизайна довольно не сложно решаемой в компонентных средах.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[19]: Функциональные типы (параллельная ветка)
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.06.05 22:31
Оценка: 1 (1) +1 :))) :))
Здравствуйте, eao197, Вы писали:
E>У C++, к счастью, есть компиляторы и кроме Visual C++.
И? Ты думаешь, с ними дело обстояло существенно лучше?
E>Кстати, официального релиза generic-ов в Java и C# разработчикам пришлось ждать и после наступления XXI века.
Гм. В Java внесли шаблоны на 12м году ее развития. В C# — на четвертом. Сколько уже лет плюсам? Такими темпами Бъярни успеет только руками развести: "мы тооолько заседание открыыыли, а ониии как ломануууулись...."
E>А ты бы смог сделать делегаты, создавая компилятор C#?
E>Я это к тому, что всем должны заниматься профессионалы в своей области. Ведь boost в C++ пишут очень продвинутые люди.
Совершенно согласен! Но ведь нифига не хотят профессионалы давать делегаты в плюсах. Их просят, а они не хотят. А я ни за пятнадцать минут не напишу ни за час. Тупить буду, ошибаться. Совместимыми потом мои делегаты с чужими не будут. Нвфиг надо? А буст — оно, конечно, хорошо. Вот только нестандартно пока нифига.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Функциональные типы (параллельная ветка)
От: vdimas Россия  
Дата: 27.06.05 22:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, vdimas, Вы писали:


V>>Знаешь, в чем неверный поинт. В том, что этого примитива нет и в дотнете. Там есть вполне реальный экземпляр вполне реального класса — делегата с конкретным типом. В С++ непринято создавать объекты "за ширмой", сама философия языка такая, что ты полностью контроллируешь процесс описания классов и создания их экземпляров (и уж тем более, ничего "само" не генерится в run-time). И создание делегата в т.ч.


V>>Вот тебе простая суть решения и я не вижу горы кода:

S>К сожалению, во-первых простая суть решения — это капля в море. Как только ты начнешь поддерживать различные сигнатуры делегатов, а также комбинирование в multicast делегаты, то тебе сразу станет поскушнее.

В приведенной ссылке на полную версию — именно мультикаст.

S>Во-вторых,

V>>В общем, Страуструп просто напросто не стал ограничивать возможности разработчика автоматически "за кулисами" генеренным классом.
S>Да-да. Поэтому разработчики, так классно неограниченные Страуструпом, должны были сначала дождаться введения шаблонов в С++, а потом сидеть и куковать до XXI века, пока компиляторы не научились делать частичную специализацию. В каком году вышел VC++ 7.0? Предыдущую версию компилятора ты небрежно оставил за бортом.

Без частичной специализации обходились чем-то вроде Delegate2, Delegate3 и т.д. шаблонами (на разное колич-во аргументов). Неудобств это не вызывало, т.к. напрямую мало кто использует, обычно применяют typedef. Через этот typedef можно подставить вообще произвольную имплементацию, т.к. сигнатура вызова обычно одинакова — через operator().

Далее. Не так часто нужен именно делегат класса. В большинстве случаев достаточно было callback-функции. Событийная модель ГУИ тоже не сразу выросла из первоначальных штанишек. Достаточно посмотреть, как это сделано в Java безо всяких делегатов. А "крупномасштабное" применение делегатов я впервые увидел в QT задолго до .Net (их слоты и сигналы), а на чем QT написан, ты знаешь.

V>>В общем, твой поинт немного неккоректен по отношению к целям и задачам С++, и более того — технически нереализуем.

S>В общем, твой поинт немного некорректен. См., напр. здесь
Автор(ы): Александр Шаргин
Дата: 19.03.2003
Делегаты в CLR удобны, типобезопасны и эффективны. Последнее время на форумах RSDN часто поднимается вопрос о том, можно ли реализовать делегаты с аналогичными свойствами, оставаясь в рамках "чистого" C++. Оказывается, это вполне возможно. В этой статье я покажу, как это сделать.
. Вот он, кросс-платформенный делегат. При помощи шабонов, макросов, копи-пейста и такой-то матери. Я, конечно, далеко не гуру С++. Но написать такое за 15 минут я бы не смог.


В приведенном примере делается попытка мимикрирования делегата дотнета. Делается несколько допущений и затем с честью решаются встреченные трудности. Непонятно только — зачем?
(вопрос автору той статьи — а так ли часто мы "отписываемся" от события? если ответ — редко, то не легче ли в этих редких случаях сохранить экземпляр самого делегата и отписать именно его?)

Сходи лучше по этой ссылке http://www.rsdn.ru/Forum/Message.aspx?mid=908731&amp;only=1
Автор: vdimas
Дата: 21.11.04
, даже если ты не гуру, то суть уловишь. Имплементация приведена вся, и без макросов.

Сложность реализации boost::bind вызвана несколькими причинами:
— поддержка различных call convertions,
— поддержка более старых компиляторов.

----------
кстати, тут ругали Страуструпа, типа "недодумал"...

нууу, Microsoft, как всегда, додумала. Взгляните в MSDN на ключевое слово __event в их С++ компиляторе, начиная с 7.0

Вроде — красиво, почти как VlaD2 хотел... Но там несколько неудачных моментов, как раз наглядно иллюстрирующих, почему эту фичу не стоило вносить в язык. (хинт — этот __event раскрывается препроцессором в гору кода, в Debug-режиме прекрасно видно, во что именно, а главное — понятно, почему именно так)
Re[18]: Функциональные типы (параллельная ветка)
От: Павел Кузнецов  
Дата: 28.06.05 00:07
Оценка:
Sinclair,

> V>В общем, Страуструп просто напросто не стал ограничивать возможности разработчика автоматически "за кулисами" генеренным классом.


> Да-да. Поэтому разработчики, так классно неограниченные Страуструпом, должны были сначала дождаться введения шаблонов в С++,


Ну, шаблоны в C++ появились преизрядно давно.

> а потом сидеть и куковать до XXI века, пока компиляторы не научились делать частичную специализацию.


Данный пример легко переписывается без использования частичной специализации.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[18]: Функциональные типы (параллельная ветка)
От: Cyberax Марс  
Дата: 28.06.05 04:04
Оценка: 8 (1) +2
Sinclair wrote:

> V>Вот тебе простая суть решения и я не вижу горы кода:

> К сожалению, во-первых простая суть решения — это капля в море. Как
> только ты начнешь поддерживать различные сигнатуры делегатов, а также
> комбинирование в multicast делегаты, то тебе сразу станет поскушнее.
> Во-вторых,

Для этого есть boost.signals, в котором проблемы мультикастов решена
наааамного лучше, чем в CLR. Скажем, в boost.signals есть поддержка
combiner'ов, которые позволяют произвольным образом комбинировать
результаты выполнения делегатов. Еще есть поддержка приоритетов вызова
обработчиков и т.п.

> V>В общем, Страуструп просто напросто не стал *ограничивать*

> возможности разработчика автоматически "за кулисами" генеренным классом.
> Да-да. Поэтому разработчики, так классно неограниченные Страуструпом,
> должны были сначала дождаться введения шаблонов в С++, а потом сидеть
> и куковать до XXI века, пока компиляторы не научились делать частичную
> специализацию. В каком году вышел VC++ 7.0? Предыдущую версию
> компилятора ты небрежно оставил за бортом.

А у нас уже есть _релиз_ VisualStudio с поддержкой анонимных функций и
генериков?

> V>В общем, твой поинт немного неккоректен по отношению к целям и

> задачам С++, и более того — технически нереализуем.
> В общем, твой поинт немного некорректен. См., напр. здесь
> <http://rsdn.ru/article/cpp/delegates.xml&gt;
Автор(ы): Александр Шаргин
Дата: 19.03.2003
Делегаты в CLR удобны, типобезопасны и эффективны. Последнее время на форумах RSDN часто поднимается вопрос о том, можно ли реализовать делегаты с аналогичными свойствами, оставаясь в рамках "чистого" C++. Оказывается, это вполне возможно. В этой статье я покажу, как это сделать.
. Вот он,

> кросс-платформенный делегат. При помощи шабонов, макросов, копи-пейста
> и такой-то матери. Я, конечно, далеко не гуру С++. Но написать такое
> за 15 минут я бы не смог.

Ну так и не надо, а вот взять boost.signals, прочесть доку на нее и
начать использовать — вполне можно.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[20]: Функциональные типы (параллельная ветка)
От: Cyberax Марс  
Дата: 28.06.05 04:31
Оценка: 1 (1) +3
Sinclair wrote:

> E>Кстати, официального релиза generic-ов в Java и C# разработчикам

> пришлось ждать и после наступления XXI века.
> Гм. В Java внесли шаблоны на 12м году ее развития. В C# — на
> четвертом. Сколько уже лет плюсам? Такими темпами Бъярни успеет только
> руками развести: "мы тооолько заседание открыыыли, а ониии как
> ломануууулись...."

В С++ были шаблоны еще в 80-х годах, как раз на 4-5 году жизни. И в
отличие от С#/Java у Страуструпа не было перед глазами примера
промышленного языка, в котором уже реализован механизм шаблонов (разве
что генерик-модули в Аде). Все шишки комитет С++ набивал самостоятельно.

> E>А ты бы смог сделать делегаты, создавая компилятор C#?

> E>Я это к тому, что всем должны заниматься профессионалы в своей
> области. Ведь boost в C++ пишут очень продвинутые люди.
> Совершенно согласен! Но ведь нифига не хотят профессионалы давать
> делегаты в плюсах. Их просят, а они не хотят.

boost.function+boost.bind, для мультикаста — boost.signals. Что еще вам
надо?

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[19]: Функциональные типы (параллельная ветка)
От: Cyberax Марс  
Дата: 28.06.05 04:33
Оценка: 3 (1) +3
vdimas wrote:

> Вроде — красиво, почти как VlaD2 хотел... Но там несколько неудачных

> моментов, как раз наглядно иллюстрирующих, почему эту фичу не стоило
> вносить в язык. (хинт — этот __event раскрывается препроцессором в
> гору кода, в Debug-режиме прекрасно видно, во что именно, а главное —
> понятно, почему именно так)

Более того, с __event'ами получаются еще и ооооооооочень большие
проблемы в многотредовом режиме. Лично шишки набивал

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[21]: Функциональные типы (параллельная ветка)
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.06.05 06:18
Оценка: :))
Здравствуйте, Cyberax, Вы писали:
C>В С++ были шаблоны еще в 80-х годах, как раз на 4-5 году жизни.
Я в курсе. Зато делегатов нет до сих пор, а в C# они — с рождения.

C>boost.function+boost.bind, для мультикаста — boost.signals. Что еще вам

C>надо?
Стандарта.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: Функциональные типы (параллельная ветка)
От: Cyberax Марс  
Дата: 28.06.05 06:25
Оценка: +3
Sinclair wrote:

> C>В С++ были шаблоны еще в 80-х годах, как раз на 4-5 году жизни.

> Я в курсе. Зато делегатов нет до сих пор, а в C# они — с рождения.

В чем отличия boost.bind+boost.function от делегата?

> C>boost.function+boost.bind, для мультикаста — boost.signals. Что еще вам

> C>надо?
> Стандарта.

А зачем? Или вы будете ждать утверждения стандарта ISO на C# 2.0, прежде
чем начать использовать его?

Вот уж поклонники МС про стандарты лучше бы вообще молчали.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[23]: Функциональные типы (параллельная ветка)
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.06.05 06:59
Оценка:
Здравствуйте, Cyberax, Вы писали:
C>чем начать использовать его?
Ну почему ISO? Мне вполне хватит ECMA. Думаю, за ним не заржавеет.
C>Вот уж поклонники МС про стандарты лучше бы вообще молчали.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Функциональные типы (параллельная ветка)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 28.06.05 07:34
Оценка: 3 (1) +1 :)
Здравствуйте, VladD2, Вы писали:

VD>Знашь, я заметил за тобой одну проблему которая все время убивает дискуссию. Ты излишне переусложняешь свои примеры.


May be, may be.

Но хочу заметить, что в форуме я стараюсь приводить максимально облегченные примеры. В реальной работе все несколько сложнее. И C++ помогает с этой сложностью бороться (хотя у тебя и другая точка зрения). А вот ты часто разбавляешь свои ответы популистскими высказываниями и требованиями "быть проще". Что так же не способствует конструктивному диалогу.

VD>Какие-то конечные автоматы... Надо выделить основную сущьность и показать ее суть, а не наворачивать огромные примеры в которых сложно разобраться (а точнее очень не хочется разбираться).


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

Имхо, суть указателя на метод именно в том, что это указатель на метод. Который можно применить к любому объекту этого типа (и к любому объекту производного типа). Делегат же, насколько я понял из твоих объяснений, это тот же указатель, который намертво связан с конкретным объектом. Поэтому применить делегат к другому объекту этого же типа уже не реально (правильно?).

И спрашивается: какой подход является более общим? Имхо, C++ный. Т.к. функциональность делегата (указатель на метод + указатель на объект) мы можем очень просто сделать. И при этом имеем возможность работать с указателями на методы, не привязанными к конкретным объектам (где-то я такое использовал, при острой необходимости могу поискать). А вот от делегата таких возможностей мы вряд ли дождемся.

VD>Твой пример изумительно решается как на на обычных указателях на глобальны функции (не методы класса), так и на делегатах. Вот реализация на делегатах:

<...пример поскипан...>
VD>Основная суть здесь использование указателя на метод в качестве ссылки на алгоритм.

Ничего принципиально другого, кроме синтаксиса, по сравнению со своим примером я не увидел.

VD>Так вот, твой случай частный, так как ссылка нужна в рамках класса. И как частный случай он легко реализуется на делегатах. Но твой случай можно усложнить введя требование возможности передачи управления в любой объект, ане только этот же или даже в объекты этого же типа. И тут делегаты опять таки дают чистое и красивое решение, а указатели на методы класса пасуют.


Это как же указатели на методы пасуют?

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


И чем же ты это предположение опровергаешь?

VD>Все что нужно сделать, это убрать этот лишний контроль.


А вот это от тебя по отношению к C++ вообще странно слышать: то тебе контроля со стороны C++ мало, а тут -- убрать лишний контроль нафиг! Так чему верить-то?

VD>Ага. Очень хороший прием. И в Шарпе он используется на право и на лево. Причем выглядит очень компактно и красиво. В сочетании с рефлекшоном он позволяет просто таки творить чудеса. Вот, например, простенький пример крэкера сообщений написанного на C# Крекер Windows-сообщений в AOP-стиле
Автор: VladD2
Дата: 11.05.04
. Благодаря мапу делегатов, рефлекшону и атрибутам обработку сообщений виндовс получилось сделать декларативным:

VD>
VD>// Костанты-идентификаторы сообщений. Можно надыбать на www.codeproject.com или в winuser.h
VD>const int WM_LBUTTONDOWN   =  0x0201;
VD>const int WM_RBUTTONDOWN   =  0x0204;

VD>// Обработчики сообщений. Атрибутом WinMsg с ними ассоциируются сами сообщения.

VD>[WinMsg(WM_RBUTTONDOWN)]
VD>bool OnRButtonDown(ref Message msg)
VD>{
VD>        BackColor = Color.Blue;
VD>        return true;
VD>}

VD>[WinMsg(WM_LBUTTONDOWN)]
VD>bool OnLButtonDown(ref Message msg)
VD>{
VD>        BackColor = Color.Red;
VD>        return true;
VD>}
VD>


Влад, я уже давно не программировал на WinAPI, но даже в низопамятные времена получать в обработчик сообщения голый Message, а затем извлекать из wParam, lParam нужные значения было каменным веком. Даже если не брать обработчиков сообщений в MFC, то были замечательные макросы из windowsx.h. Поэтому приведенный здесь фрагмент может быть и выглядит красивым с точки зрения использования атрибутов .Net, но я бы хотел работать с обработчиками сообщений вида:
void    OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
void    OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
void    OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);

и т.д.

VD>А вот пример из редактора кода. Он довольно длинный, так что сначало опишу его применение, а потом приведу код.


VD>Этот пример показывает как можно довести обработку клавиатурный ввод до полной декларативности и позволить задавать их не в программе, а в конфигурационном файле.


Не вижу в этом коде ничего выдающегося. Подобные решения на C++ существовали еще за десять лет до появления C#.
Конечно, в C++ для него потребовалось бы вручную поддерживать таблицу имен функций и соответствующие им имена. В C# это делается автоматом при помощи рефлекшена. Удобно? На первый взгляд, может быть. Но чем за это платить приходится? У меня в программах сотни типов и только для очень малой их части нужна информация для рефлекшена. Поэтому я вполне в состоянии эту информацию предоставить сам. Кроме того, вот что ты будешь делать, если тебе потребуется имя метода CaretVirtualLineUp переименовать? Будешь править все конфиги? А что, если для имени "CaretVirtualLineUp" тебе потребуется либо подставлять метод CaretVirtualLineUpClassic, либо метод CaretVirtualLineUpWrappedView, или CaretVirtualLineUpRestructedView в зависимости от других настроек? Где тогда рефлекшен окажется?

VD>А вот так выглядит реализация:


<...Пример поскипан...>

Влад, я что-то не понял, а почему XML вручную читается? А где же хваленая прозрачная XML сериализация? Где XSD, которая бы позволила проверять семантическую корректность? И что будет, если в атрибуте Key задать, например: "Control | Alt | Control | Alt | Control"?

В моем подходе к парсингу конфигов класс для разбора одного Shortcat-a выглядел бы так:
using namespace cls_2;

class    tag_shortcat_t : public tag_no_value_t
    {
    private :
        tag_scalar_t< std::string >    m_key;
        tag_scalar_t< std::string >    m_action;
        tag_no_value_t    m_ctrl;
        tag_no_value_t    m_alt;
        tag_no_value_t    m_shift;
    public :
        // Формат конструктора диктуется требованиями класса tag_vector_of_tags_t.
        tag_shortcat_t( const char * name, bool is_mandatory )
            :    tag_no_value_t( name, is_mandatory, true )
            ,    m_key( self_tag(), "key", true )
            ,    m_action( self_tag(), "action", true )
            ,    m_ctrl( m_key, "ctrl", false )
            ,    m_alt( m_key, "alt", false )
            ,    m_shift( m_key, "shift", false )
            {}
            
        // Код метода по извлечению значений из m_key, m_action, m_ctrl, m_alt и m_shift по вкусу :)
        shortcat_t query_value() const
        {
            return shortcat_t( m_key.query_value(), m_action.query_value(),
                ( m_ctrl.is_defined() ? VK_CTRL : 0 ) |
                ( m_alt.is_defined() ? VK_ALT : 0 ) |
                ( m_shift.is_defined() ? VK_SHIFT : 0 ) );
        }
    };


И такой класс позволил бы разбирать конфиги вида:
|| Навигация по тексту
{shortcat {key "Up"}                    {action "CaretVirtualLineUp"}}
{shortcat {key "Down"}                  {action "CaretVirtualLineDown"}}
{shortcat {key {shirt} "Up"}            {action "CaretVirtualLineUpExtend"}}
{shortcat {key {shift} "Down"}          {action "CaretVirtualLineDownExtend"}}
{shortcat {key "Home"}                  {action "CaretVirtualLineHome"}}
{shortcat {key "End"}                   {action "CaretVirtualLineEnd"}}
{shortcat {key {shift} "Home"}          {action "CaretVirtualLineHomeExtend"}}
{shortcat {key {shift} "End"}           {action "CaretVirtualLineEndExtend"}}
{shortcat {key "Left"}                  {action "CaretLeft"}}
{shortcat {key {ctrl} "Left"}           {action "CaretWordLeft"}}
{shortcat {key {ctrl} "Right"}          {action "CaretWordRight"}}
{shortcat {key {shirt} {ctrl} "Left"}   {action "CaretWordLeftExtend"}}
...
|| Клипборд
{shortcat {key {ctrl}  "C"}             {action "Copy"}}
{shortcat {key {ctrl}  "Insert"}        {action "Copy"}}
{shortcat {key {ctrl}  "V"}             {action "Paste"}}
{shortcat {key {shift} "Insert"}        {action "Paste"}}
{shortcat {key {ctrl}  "X"}             {action "Cut"}}
{shortcat {key {shift} "Delete"}        {action "Cut"}}


При этом код по извлечению значений из конфига будет работать только с именем типа tag_shortcat_t и его методом query_value(). И поэтому не будет привязан ни к конкретным именам тегов, ни к конкретной реализации самого тега (например, можно прозрачным образом ввести псевдонимы для ctrl, alt, shift. Или даже сменить формат shortcat-а на такой:
{shortcat
        {key {ctrl}  "C"}
        {key {ctrl}  "Insert"}
    {action "Copy"}}
{shortcat
        {key {ctrl}  "V"}
        {key {shift} "Insert"}
    {action "Paste"}}
{shortcat
        {key {ctrl}  "X"}
        {key {shift} "Delete"}
    {action "Cut"}}


Так что по объему мой велосипедный подход не сильно больше твоего, а вот в сопровождении, имхо, мой удобнее.

VD>Так что указатели на методы класса в С++ — это всего лишь досадное недоразумение. Создание частного случая вместо реализации общего.


VD>Ну, а все эти сигналы, бинды, фаншоны из С++ не боллее чем попытка обойти достадные ограничения за счет возможностей метапрограммирования и нехилых изысков.


Я, напротив, думаю, что это пример того, как используя небольшой набор языковых возможностей получить более высокоуровневые средства. А вот в C# для этого пришлось язык расширять.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[19]: Функциональные типы (параллельная ветка)
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 28.06.05 08:54
Оценка:
Здравствуйте, eao197, Вы писали:

E>Делегат же, насколько я понял из твоих объяснений, это тот же указатель, который намертво связан с конкретным объектом. Поэтому применить делегат к другому объекту этого же типа уже не реально (правильно?).


Так, попробуем отделить мухи от котлет.
1) Делегат это тип. Тип, разумеется, ни к каким экземплярам вобще не привязан.
2) Экземпляр делегата может быть привязан к экземпляру. А может и на статический метод ссылаться. Единственное требование это совпадение сигнатур.
3) Смысла в вызове экземпляра делегата для другого объекта я вобще не вижу. Проще создать для него другой экземпляр того же делегата.

E>И спрашивается: какой подход является более общим? Имхо, C++ный. Т.к. функциональность делегата (указатель на метод + указатель на объект) мы можем очень просто сделать. И при этом имеем возможность работать с указателями на методы, не привязанными к конкретным объектам (где-то я такое использовал, при острой необходимости могу поискать). А вот от делегата таких возможностей мы вряд ли дождемся.


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

E>Влад, я уже давно не программировал на WinAPI, но даже в низопамятные времена получать в обработчик сообщения голый Message, а затем извлекать из wParam, lParam нужные значения было каменным веком. Даже если не брать обработчиков сообщений в MFC, то были замечательные макросы из windowsx.h. Поэтому приведенный здесь фрагмент может быть и выглядит красивым с точки зрения использования атрибутов .Net, но я бы хотел работать с обработчиками сообщений вида:

E>
E>void    OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
E>void    OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
E>void    OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
E>

E>и т.д.

А так в винформсах и есть. Влад тебе о другом говорил — о решении проблем диспетчеризации декларативно. А уж что там диспетчеризуется не столь важно. Вот тебе другой пример не буду говорить откуда: обработка сообщений от декларативно описываемых менюх и тулбаров, а заодно и от нажатых шоткатов
[MethodShortcut(Shortcut.CtrlB, "Выделить жирным ()", "Поместить тег [b] вокруг выделенной области.")]
[StripEventHandler("select-bold")]
public void InsertBoldTag()
{
    TagClicked("b|b|0");
}

[MethodShortcut(Shortcut.CtrlI, "Выделить курсивом ([i])", "Выделить текст курсивом")]
[StripEventHandler("select-italic")]
public void InsertItalicTag()
{
    TagClicked("i|i|0");
}

[MethodShortcut(Shortcut.CtrlK, "Вставить URL", "Вставить URL")]
[StripEventHandler("insert-url")]
public void InsertUrlTag()
{
    TagClicked("url=|url|0");
}


Заметь, это весь пользовательский код.
... << RSDN@Home 1.2.0 alpha rev. 502>>
AVK Blog
Re[20]: Функциональные типы (параллельная ветка)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 28.06.05 09:13
Оценка: +1
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, eao197, Вы писали:


E>>Делегат же, насколько я понял из твоих объяснений, это тот же указатель, который намертво связан с конкретным объектом. Поэтому применить делегат к другому объекту этого же типа уже не реально (правильно?).


AVK>Так, попробуем отделить мухи от котлет.

AVK>1) Делегат это тип. Тип, разумеется, ни к каким экземплярам вобще не привязан.
AVK>2) Экземпляр делегата может быть привязан к экземпляру. А может и на статический метод ссылаться. Единственное требование это совпадение сигнатур.

Ok. Признаю, что был не прав. По незнанию.

AVK>3) Смысла в вызове экземпляра делегата для другого объекта я вобще не вижу. Проще создать для него другой экземпляр того же делегата.


Если ты его не видишь, то это не значит, что смысла нет. Я вот, иногда использовал сначала выбор указателя на метод объекта, который нужно будет вызвать в текущих условиях, затем получал откуда-то объект и применял к нему этот указатель.

E>>И спрашивается: какой подход является более общим? Имхо, C++ный. Т.к. функциональность делегата (указатель на метод + указатель на объект) мы можем очень просто сделать. И при этом имеем возможность работать с указателями на методы, не привязанными к конкретным объектам (где-то я такое использовал, при острой необходимости могу поискать). А вот от делегата таких возможностей мы вряд ли дождемся.


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


Значит ошибся по незнанию.

E>>Влад, я уже давно не программировал на WinAPI, но даже в низопамятные времена получать в обработчик сообщения голый Message, а затем извлекать из wParam, lParam нужные значения было каменным веком. Даже если не брать обработчиков сообщений в MFC, то были замечательные макросы из windowsx.h. Поэтому приведенный здесь фрагмент может быть и выглядит красивым с точки зрения использования атрибутов .Net, но я бы хотел работать с обработчиками сообщений вида:

E>>
E>>void    OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
E>>void    OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
E>>void    OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
E>>

E>>и т.д.

AVK>А так в винформсах и есть. Влад тебе о другом говорил — о решении проблем диспетчеризации декларативно.


А я ему и ответил, что нафиг мне такая декларативная диспетчеризация, если работать с ее результатами будет неудобно.

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

<...>
AVK>Заметь, это весь пользовательский код.

Похожие штуки на сигналах/слотах Qt делаются без проблем. Причем на языке, который появился задолго до самой Qt. А вот в C#, похоже, для любой фенички нужно синтаксическую фишечку добавлять Во что же он эдак лет через пять превратиться? Особенно если мейнстримом станет не web-бизнес-софт, а, скажем, встраиваемые устройства со своей спецификой.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[24]: Функциональные типы (параллельная ветка)
От: Cyberax Марс  
Дата: 28.06.05 09:37
Оценка:
Sinclair wrote:

> C>чем начать использовать его?

> Ну почему ISO? Мне вполне хватит ECMA. Думаю, за ним не заржавеет.

Я предпочитаю ISO — так как они работают по своим же стандартам качества
(в отличие от ECMA).

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.