Вопрос: как можно реализовать сабж на чистом С/С++ или в MSVC 7? В Билдере есть очень удобная штука — __closure, с ее помощью можно определить указатель на функцию — член любого класса, лишь бы параметры и возвращаемые значения совпадали (ну и соглашения по вызову). А в MSVC получается, мне еще нужно знать, и какого класса функция.... Помогите пожалуйста.
Re: Универсальный указатель на функцию-член класса
Здравствуйте Павел Кузнецов, Вы писали:
ПК>Здравствуйте AkaSaint, Вы писали:
AS>>Вопрос: как можно реализовать сабж на чистом С/С++ или в MSVC 7?
ПК>В каком контексте? Решений есть несколько, каждое хорошо на своем месте.
Контекст такой: есть класс — окно со скином, он предоставляет возможность тому, кто его использует, устанавливать свои обработчики сообщений, типа OnClose.
Re[3]: Универсальный указатель на функцию-член класса
Здравствуйте AkaSaint, Вы писали:
AS>Здравствуйте Павел Кузнецов, Вы писали:
ПК>>Здравствуйте AkaSaint, Вы писали:
AS>>>Вопрос: как можно реализовать сабж на чистом С/С++ или в MSVC 7?
ПК>>В каком контексте? Решений есть несколько, каждое хорошо на своем месте.
AS>Контекст такой: есть класс — окно со скином, он предоставляет возможность тому, кто его использует, устанавливать свои обработчики сообщений, типа OnClose.
в этом случае гораздо удобнее воспользоваться интерфейсами, заводишь структуру типа этой
class CSkinedWnd //это вроде как твой класс
{
void Connect( CSkinedWndEventsSink *pSink );
void Disconnect( CSkinedWndEventsSink *pSink );
}
что они должны делать понятно вроде
ну а теперь любой кто хочет обрабатывать события от CSkinedWnd должен наследоваться от CSkinedWndEventsSink и реализовать его методы. А в рантайме где надо приконнектится, а где надо отконнектится
это упрощенный вариант обработки событий успользуемый в COM, так что можешь почитать для ознакомления с оригиналом
Ed.ward
Re[4]: Универсальный указатель на функцию-член класса
Здравствуйте Ed.ward, Вы писали:
EW>в этом случае гораздо удобнее воспользоваться интерфейсами, заводишь структуру типа этой
EW>ну а теперь любой кто хочет обрабатывать события от CSkinedWnd должен наследоваться от CSkinedWndEventsSink и реализовать его методы. А в рантайме где надо приконнектится, а где надо отконнектится
В том то и прелесть замыкания, что наследоваться ни от кого и не надо. Достаточно совпадения сигнатур. На практике разница будет заключаться в том, что с замыканием можно использовать классы, написанные до CSkinedWndEventsSink. Без замыканий — придется для существующих классов делать еще один подкласс, который реализует эти методы CSkinedWndEventsSink.
Решение без замыканий плохо масштабируется: при большом числе sink'ов возникнут трудности с тем, как устроить их иерархию, а именно следующая проблема дизайна: определить удачный интерфейс sink'a.
EW>это упрощенный вариант обработки событий успользуемый в COM, так что можешь почитать для ознакомления с оригиналом
В отсутствие замыканий, конечно, приходится пользоваться sink'ами.
PS. Кстати, кто-нибудь знает причину, что помешало ввести замыкания в C++?
Re[3]: Универсальный указатель на функцию-член класса
Здравствуйте AkaSaint, Вы писали:
AS>Контекст такой: есть класс — окно со скином, он предоставляет возможность тому, кто его использует, устанавливать свои обработчики сообщений, типа OnClose.
Здравствуйте AkaSaint, Вы писали:
AS>Контекст такой: есть класс — окно со скином, он предоставляет возможность тому, кто его использует, устанавливать свои обработчики сообщений, типа OnClose.
Забей на эту порочную дельфовскую технологию Используй интерфейсы. Благо а С++ есть множественное наследование. ИМХО чисто идеологически это намного правильнее, чем куча неизвестно куда торчащих "указателей-событий".
Как все запущенно...
Re[4]: Универсальный указатель на функцию-член класса
Здравствуйте Vladik, Вы писали:
V>Забей на эту порочную дельфовскую технологию Используй интерфейсы. Благо а С++ есть множественное наследование. ИМХО чисто идеологически это намного правильнее, чем куча неизвестно куда торчащих "указателей-событий".
Хотелось бы услышать осмысленную мотивацию
Re[5]: Универсальный указатель на функцию-член класса
Здравствуйте Anton V. Kolotaev, Вы писали:
AVK>Хотелось бы услышать осмысленную мотивацию
Вызывается неизвестно какая функция неизвестно какого класса. Почти как void *. Да, компилятор делает необходимые проверки для обеспечения корректности вызова (соглашение вызова, число и тип параметров). Но "идеологический" смысл теряется. В конце концов приходим к запутанным цепочкам таких недекларированных связей между классами.
Как все запущенно...
Re[6]: Универсальный указатель на функцию-член класса
Здравствуйте Vladik, Вы писали:
V>Здравствуйте Anton V. Kolotaev, Вы писали:
AVK>>Хотелось бы услышать осмысленную мотивацию
V>Вызывается неизвестно какая функция неизвестно какого класса.
Разве это так плохо?
V>Почти как void *.
Разве? Следующей фразой ты сказал, что нет
V>Да, компилятор делает необходимые проверки для обеспечения корректности вызова (соглашение вызова, число и тип параметров).
V>Но "идеологический" смысл теряется.
Что это такое?
V>В конце концов приходим к запутанным цепочкам таких недекларированных связей между классами.
Хм-м-м... Такие же "недекларированные" связи получаются и при использовании шаблонов: аргумент шаблона не обязан принадлежать некой иерархии классов. Более того, ограничения на сигнатуру его методов значительно более слабые — достаточно не совпадения сигнатур, а возможности вызова (что, вообще говоря, я интенсивно использую). Замыкания позволяют менять во время исполнения и тип объекта, и его вызываемый метод. Это несколько иная идиома. Я ей не пользовался, и мне интересно, чем она плоха и чему может помешать ее наличие в С++.
Re[7]: Универсальный указатель на функцию-член класса
Здравствуйте Anton V. Kolotaev, Вы писали:
V>>Вызывается неизвестно какая функция неизвестно какого класса. AVK>Разве это так плохо?
Да.
[...] V>>Да, компилятор делает необходимые проверки для обеспечения корректности вызова (соглашение вызова, число и тип параметров). V>>Но "идеологический" смысл теряется. AVK>Что это такое?
Это материал, как минимум, одной толстой книжки по ООП.
V>>В конце концов приходим к запутанным цепочкам таких недекларированных связей между классами.
AVK>Хм-м-м... Такие же "недекларированные" связи получаются и при использовании шаблонов:
Не совсем. Шаблоны все же декларируются на этапе компиляции. __closure, как ты сам заметил, — нет.
AVK>аргумент шаблона не обязан принадлежать некой иерархии классов. Более того, ограничения на сигнатуру его методов значительно более слабые — достаточно не совпадения сигнатур, а возможности вызова (что, вообще говоря, я интенсивно использую). Замыкания позволяют менять во время исполнения и тип объекта, и его вызываемый метод. Это несколько иная идиома. Я ей не пользовался, и мне интересно, чем она плоха и чему может помешать ее наличие в С++.
Эта идиома не соответствует духу С++ с его строгой типизацией и изначальной статичностью типов.
Как все запущенно...
Re[5]: Универсальный указатель на функцию-член класса
Здравствуйте Anton V. Kolotaev, Вы писали:
AVK>В том то и прелесть замыкания, что наследоваться ни от кого и не надо. Достаточно совпадения сигнатур. На практике разница будет заключаться в том, что с замыканием можно использовать классы, написанные до CSkinedWndEventsSink. Без замыканий — придется для существующих классов делать еще один подкласс, который реализует эти методы CSkinedWndEventsSink.
А можно дать, так сказать, формальное определение замыканий? Как я понял, один из примеров — обработчики событий в VCL. И спасибо за ответ
Re[6]: Универсальный указатель на функцию-член класса
Здравствуйте AkaSaint, Вы писали:
AS>А можно дать, так сказать, формальное определение замыканий? Как я понял, один из примеров — обработчики событий в VCL. И спасибо за ответ
К сожалению, как я уже сказал в соседнем топике, я ими не пользовался.
Re[7]: Универсальный указатель на функцию-член класса
Используя closure будет использован только
_один_ обработчик. А если вдруг захочется много?
Тогда опять же придется извращаться с созданием
класса диспетчера...
Мне кажется использование подключаемых-отключаемых
обработчиков событий — более эффективная вещь.
В Java Swing они активно используются — мне очень понравилось.
А на C++ из-за множественного наследования эта технология
еще легче реализуется...
Re[8]: Универсальный указатель на функцию-член класса
От:
Аноним
Дата:
08.10.02 13:33
Оценка:
Здравствуйте Vladik, Вы писали:
AVK>>Хм-м-м... Такие же "недекларированные" связи получаются и при использовании шаблонов:
V>Не совсем. Шаблоны все же декларируются на этапе компиляции. __closure, как ты сам заметил, — нет.
Чего?
AVK>>аргумент шаблона не обязан принадлежать некой иерархии классов. Более того, ограничения на сигнатуру его методов значительно более слабые — достаточно не совпадения сигнатур, а возможности вызова (что, вообще говоря, я интенсивно использую). Замыкания позволяют менять во время исполнения и тип объекта, и его вызываемый метод. Это несколько иная идиома. Я ей не пользовался, и мне интересно, чем она плоха и чему может помешать ее наличие в С++.
V>Эта идиома не соответствует духу С++ с его строгой типизацией и изначальной статичностью типов.
Чего-чего? Ты вообще о чем? Ты вообще знаешь синтаксис __closure? Причем здесь вообще строгая типизация?
Re[9]: Универсальный указатель на функцию-член класса
Здравствуйте Аноним, Вы писали:
V>>Не совсем. Шаблоны все же декларируются на этапе компиляции. __closure, как ты сам заметил, — нет. А>Чего?
А ты кто? Чего непонятно-то?
[...] V>>Эта идиома не соответствует духу С++ с его строгой типизацией и изначальной статичностью типов. А>Чего-чего? Ты вообще о чем? Ты вообще знаешь синтаксис __closure?
Знаю.
А>Причем здесь вообще строгая типизация?
При том, что она есть в С++. Я попытался как-то аргументировать свою позицию, если тебе что-то непонятно или ты с чем-то не согласен — выражайся конкретнее.
Как все запущенно...
Re[8]: Универсальный указатель на функцию-член класса
Здравствуйте Vladik, Вы писали:
AVK>>аргумент шаблона не обязан принадлежать некой иерархии классов. Более того, ограничения на сигнатуру его методов значительно более слабые — достаточно не совпадения сигнатур, а возможности вызова (что, вообще говоря, я интенсивно использую). Замыкания позволяют менять во время исполнения и тип объекта, и его вызываемый метод. Это несколько иная идиома. Я ей не пользовался, и мне интересно, чем она плоха и чему может помешать ее наличие в С++.
V>Эта идиома не соответствует духу С++ с его строгой типизацией и изначальной статичностью типов.
Отнюдь. Семантически __closure являются разновидностью лямбда-функций со связанными аргументами, которые, в свою очередь, достаточно близки функционалам, являющимся частью стандартной библиотеки, а также предоставляемыми многочисленными сторонними библиотеками. __closure могут быть симулированы для конечного количества аргументов соответствующей библиотекой, например libsigc++. Наличие поддержки со стороны компилятора ничего "идеологически" не поменяет и строгую типизацию не нарушит.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[9]: Универсальный указатель на функцию-член класса
Здравствуйте Павел Кузнецов, Вы писали:
V>>Эта идиома не соответствует духу С++ с его строгой типизацией и изначальной статичностью типов. ПК>Отнюдь. Семантически __closure являются разновидностью лямбда-функций со связанными аргументами,
А я думал лямбда-функции только в лиспе есть...
ПК>которые, в свою очередь, достаточно близки функционалам, являющимся частью стандартной библиотеки, а также предоставляемыми многочисленными сторонними библиотеками.
Как ты лихо от одного к совершенно другому пришел
ПК>__closure могут быть симулированы для конечного количества аргументов соответствующей библиотекой, например libsigc++. Наличие поддержки со стороны компилятора ничего "идеологически" не поменяет и строгую типизацию не нарушит.
Может я чего не понимаю, тогда поправьте меня. Вот простейший "аналог" __closure на классическом С++ (как я его себе представляю):
// аналог __closure для функции с одним аргументомtemplate <typename T, typename R, typename A1>
class closure1_t
{
T &m_this;
R (T::*m_f)(A1);
public:
closure1_t(T &t, R (T::*f)(A1)):m_this(t), m_f(f){}
R operator () (A1 a1){ return (m_this.*m_f)(a1);}
};
...
closure1_t<X, int, char> closure1(x, &X::f);
closure1('a'); // вот чего мы хотели
Принципиальной вещью, добавляющей ту самую "статичность" типов является наличие в объявлении в качестве параметра шаблона X — класса, на функцию которого мы завязываемся. В случае __closure в C++Builder — класс X неизвестен. Что для С++ будет "грязным хаком" (выделено жирным):
template <typename R, typename A1>
class unk_closure1_t
{
class T{};
T &m_this;
R (T::*m_f)(A1);
public:
template <typename UnkT>
unk_closure1_t(UnkT &t, R (UnkT::*f)(A1))
:m_this(reinterpret_cast<T &>(t)),
m_f(reinterpret_cast<R (T::*)(A1)>(f)){}
R operator () (A1 a1){ return (m_this.*m_f)(a1);}
};
...
unk_closure1_t<int, char> __closure1(x, &X::f);
__closure1('a'); // типа должно работать ;)
Как все запущенно...
Re[10]: Универсальный указатель на функцию-член класса
Здравствуйте Vladik, Вы писали:
V>А я думал лямбда-функции только в лиспе есть... ;)
Не только. На уровне языка C++, конечно, их не поддерживает, но соответствующие библиотеки имеются.
ПК>>__closure могут быть симулированы для конечного количества аргументов соответствующей библиотекой, например libsigc++. Наличие поддержки со стороны компилятора ничего "идеологически" не поменяет и строгую типизацию не нарушит.
V>Может я чего не понимаю, тогда поправьте меня. Вот простейший "аналог" __closure на классическом С++ (как я его себе представляю):
V>
V>// аналог __closure для функции с одним аргументом
V>template <typename T, typename R, typename A1>
V>class closure1_t
V>{
V> T &m_this;
V> R (T::*m_f)(A1);
V>public:
V> closure1_t(T &t, R (T::*f)(A1)):m_this(t), m_f(f){}
V> R operator () (A1 a1){ return (m_this.*m_f)(a1);}
V>};
V>...
V>closure1_t<X, int, char> closure1(x, &X::f);
V> closure1('a'); // вот чего мы хотели
V>
V>Принципиальной вещью, добавляющей ту самую "статичность" типов является наличие в объявлении в качестве параметра шаблона X — класса, на функцию которого мы завязываемся. В случае __closure в C++Builder — класс X неизвестен. Что для С++ будет "грязным хаком" (выделено жирным):
Приведенный пример можно переписать иначе, скрыв информацию о классе Х от того, кто будет вызывать closure (схематичный код, не претендующий на правильность и/или полноту):
struct AbstractSignal
{
virtual ~AbstractSignal() { }
};
template<class R, class A1>
struct AbstractSignal1
{
virtual R operator()(A1) const = 0;
};
// RTTI-обертка для AbstractSignal1, предназначена для использования конечным пользователемtemplate<class R, class A1>
class Signal1
{
public:
Signal1(AbstractSignal1<R, A1>* signal = 0) : m_signal (signal) { }
R operator()(A1 a1) const { return (*m_signal)(a1); }
private:
// *** здесь должен быть какой-нибудь smart-pointer с подсчетом ссылок ***
AbstractSignal1<R, A1>* m_signal;
};
template<class R, class T, class A1>
class MemFun1Closure1 : public AbstractSignal1<R, A1>
{
public:
MemFun1Closure1(R (T::*f)(A1), T& t) : m_f (f), m_this (t) { }
R operator()(A1 a1) const { return (m_this.*m_f)(a1); }
private:
R (T::*m_f)(A1);
T& m_this;
};
template<class R, class A1, class T>
Signal1<R, A1> closure(R (T::*f)(A1), T& t)
{
return Signal1<R, A1>(new MemFun1Closure1<R, T, A1>(f, t));
}
Таким образом, пользователь не будет знать о классе T ни в момент вызова, ни в месте объявления signal:
class X
{
public:
int foo(double);
};
int main()
{
// В этой точке информация о классе X не нужна: Signal1 может быть проинициализирован и обычной функцией, если реализовать соответствующий PtrFunXClosure1.
Signal1<int, double> signal;
// . . .
// Это единственное место, где пользователь упоминает класс X.
X x;
signal = closure(&X::foo, x);
// . . .
// В этой точке информация о существовании класса X пользователю тоже не нужна.return signal(0.5);
}
Поддержка со стороны компилятора потенциально позволяет избавиться от динамического распределения памяти (или спрятать его), но ничего не меняет принципиально.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[10]: Универсальный указатель на функцию-член класса
Здравствуйте Vladik, Вы писали:
V>Может я чего не понимаю, тогда поправьте меня. Вот простейший "аналог" __closure на классическом С++ (как я его себе представляю):
Все это интересно и полезно, но тем не менее ни одно решение на классическом С++ не позволяет, к сожалению, закрыть, наконец, пропасть между функциональными объектами и функциями, т.е. написать реентерабельный closure, результатом котрого была бы обычная функция (указатель на функцию), а не функциональный объект. Платформенно-зависимые решения, разумеется, присутствуют. Портабельных решений нет.