Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

[C++] Иерархия функторов

358  1 2 3 все
Simple Nothing is f*cked08.11.06 10:11
Simple
08.11.06 10:11 
Задача: для каждого элемента массива нужно выполнить определенную операцию. В некоторых случаях эта операция отличается от стандартной. Напрашивается иерархия функторов, но у Саттера я прочитал:
В ответ на:
Therefore, function objects must be cheap to copy and monomorphic. But large and/or polymorphic objects are useful, and using them is okay; just hide the size and richness using the Pimpl idiom, which leaves the outer class as the required cheap-to-copy monomorphic type that still accesses rich state.

Идея, в принципе, ясна, но если я не ошибаюсь, разные копии функтора пользуются одним и тем же внутренним объектом Pimpl. Получается, что нужно наследовать не от самого базового функтора, а от Pimpl'а, так ведь?
Существуют какие-то другие паттерны для решения такой проблемы?
You said it man... Nobody f*cks with the Jesus (c)
Все о бильярде
#1 
Murr коренной житель08.11.06 10:25
Murr
NEW 08.11.06 10:25 
в ответ Simple 08.11.06 10:11
В некоторых случаях эта операция отличается от стандартной.
-----
Хммм... если она отличается для элементов производного типа, то не вижу никаких проблем - все разруливается по типам.
#2 
Simple Nothing is f*cked08.11.06 10:33
Simple
NEW 08.11.06 10:33 
в ответ Simple 08.11.06 10:11
Элементы массива преобразовываются в элементы некоего документа. Операция преобразования может отличаться для разных типов документов и состояния конкретного элемента массива. То есть, желательно бы для каждого типа документа сделать отдельный функтор.
По идее, можно было бы сделать одну виртуальную функцию и вызывать ее из operator(), а остальное упрятать в Pimpl. Как это будет?
#3 
Simple Nothing is f*cked08.11.06 10:34
Simple
NEW 08.11.06 10:34 
в ответ Murr 08.11.06 10:25
Проблема в том, что объект-функтор копируется внутри стандартного алгоритма.
#4 
  scorpi_ скептик08.11.06 10:36
NEW 08.11.06 10:36 
в ответ Simple 08.11.06 10:33
Элементы массива преобразовываются в элементы некоего документа.
Это имхо классическая область применения визитора.
#5 
Murr коренной житель08.11.06 10:37
Murr
NEW 08.11.06 10:37 
в ответ Simple 08.11.06 10:34
Эээ... Пишешь конструктор копирования и делаешь его виртуальным. Ну или строишь что-то, что будет разбираться с типами...
#6 
Simple Nothing is f*cked08.11.06 10:57
Simple
NEW 08.11.06 10:57 
в ответ scorpi_ 08.11.06 10:36
То, что я собираюсь сделать, и есть визитор для одного типа элементов, разве не так? Пробежать по массиву, для каждого элемента вызвать абстрактную операцию.
#7 
  scorpi_ скептик08.11.06 11:23
NEW 08.11.06 11:23 
в ответ Simple 08.11.06 10:57
Ты банду читал?
#8 
Simple Nothing is f*cked08.11.06 11:31
Simple
NEW 08.11.06 11:31 
в ответ scorpi_ 08.11.06 11:23
Да. Щас перечитал ту главу для верности. В данном случае, типов элементов у меня один, а визиторов столько, сколько типов документов, правильно? Не пойму, в чем отличие паттерна от for_each.
#9 
  scorpi_ скептик08.11.06 11:33
NEW 08.11.06 11:33 
в ответ Simple 08.11.06 11:31
в double dispatch
#10 
Simple Nothing is f*cked08.11.06 12:18
Simple
NEW 08.11.06 12:18 
в ответ scorpi_ 08.11.06 11:33
Разницу понял, буду думать дальше. Спасибо.
#11 
rahimov80 завсегдатай13.11.06 16:29
NEW 13.11.06 16:29 
в ответ Simple 08.11.06 10:11
У меня некоторые проблемы с терминологией (ну или отсутсвие необходимых знаний ), не сможет ли кто нибудь помочь? (только не немецкий - english пожалуйста )
"объект-функтор, иерархия функторов" - это что?
"банда" - это что?
* Band ?
* Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides ?
"for_each" - это паттерн iterator?
#12 
  scorpi_ 13.11.06 17:38
NEW 13.11.06 17:38 
в ответ rahimov80 13.11.06 16:29
"объект-функтор, иерархия функторов" - это что?
http://en.wikipedia.org/wiki/Function_object
"банда" - это что?
GoF, Gang of Four, Gamma & Co.
"for_each" - это паттерн iterator?
функция из STL - http://www.sgi.com/tech/stl/for_each.html
#13 
Simple Nothing is f*cked14.11.06 10:55
Simple
NEW 14.11.06 10:55 
в ответ scorpi_ 13.11.06 17:38
Читая "101 rules...", я вдруг понял, почему моя первоначальная задумка не получилась бы: функтор передается по значению и был бы порезан. Visitor фунциклирует, спасибо еще раз за наводку.
Почему-то у нас в фирме существует непонятное отторжение стандартной библиотеки. Пытаюсь пробить стену :)
#14 
rahimov80 завсегдатай17.11.06 14:15
NEW 17.11.06 14:15 
в ответ scorpi_ 13.11.06 17:38, Последний раз изменено 17.11.06 17:19 (rahimov80)
Спасибо большое за быстрый и полный (по крайней мере знаешь, где и что искать) ответ.
В ответ на:

"объект-функтор, иерархия функторов" - это что?
http://en.wikipedia.org/wiki/Function_object

С трудом, но разобрался: слабо понимаю motivation. Использовать для
for_each - вообще ограниченние, т.к. for_each не допускает полиморфизм для функтора
В ответ на:

"банда" - это что?
GoF, Gang of Four, Gamma & Co.

Век живи, век учись: "банда четырёх" - во как.
Да. Я тоже люблю сокрашения, раньше говорил: "Иди ARM почитай", а сейчас буду говорить:"иди GoF почитай"!
В ответ на:

"for_each" - это паттерн iterator?
функция из STL - http://www.sgi.com/tech/stl/for_each.html

А я уже, грешным делом, подумал, что это "For Each element In group" в VB
До сих пор как в страшном сне:
[propget, id(1), helpstring("property Count")] HRESULT Count([out, retval] long *pVal);
[propget, restricted, id(DISPID_NEWENUM), helpstring("property _NewEnum")] HRESULT _NewEnum([out, retval] LPUNKNOWN *pVal);
ну и
typedef CComObject< CComEnum <IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> > > Enum;

#15 
rahimov80 завсегдатай17.11.06 15:37
NEW 17.11.06 15:37 
в ответ rahimov80 17.11.06 14:15, Последний раз изменено 17.11.06 17:18 (rahimov80)
В ответ на:
С трудом, но разобрался: слабо понимаю motivation. Использовать для
for_each - вообще ограниченние, т.к. for_each не допускает полиморфизм для функтора

Прошу прошения, я конечно нашёл полиморфизм для функтора в for_each
#16 
Simple Nothing is f*cked19.11.06 20:16
Simple
NEW 19.11.06 20:16 
в ответ rahimov80 17.11.06 15:37
Динамический полиморфизм работает только с указателями и ссылками, а в алгоритм функтор обычно передается по значению.
#17 
rahimov80 завсегдатай20.11.06 12:09
NEW 20.11.06 12:09 
в ответ Simple 19.11.06 20:16
Я не понимаю Ваше замечание.
Что значит "обычно"? Так
for_each<list<CNode>::iterator, CBaseFactor&>(lstCNode.begin(), lstCNode.end(), theFactor)
я могу использовать? Вот и ссылка.
#18 
Simple Nothing is f*cked20.11.06 12:31
Simple
NEW 20.11.06 12:31 
в ответ rahimov80 20.11.06 12:09
Давай на "ты". На "вы" в сети общаться не принято :)
Такой пример:
class Base
{
virtual void foo();
}
class Derived : public Base
{
virtual void foo();
}
void f(Base a)
{
a.foo();
}
int main()
{
Base b;
f(b);
Derived d;
f(d);
}


Какая из foo будет вызвана во втором случае, Base::foo или Derived::foo?

#19 
rahimov80 завсегдатай20.11.06 12:41
NEW 20.11.06 12:41 
в ответ Simple 20.11.06 12:31
Simple, если на ты, то не надо меня "лечить" (прошу прошения за жаргон)
В твоем случае будет вызванна Base::foo()
Но если определение void f(Base& a){ a.foo();}, то будет вызванна Derived::foo(). Возражений нет?
А значок & во втором параметре для for_each видел?
#20 
Simple Nothing is f*cked20.11.06 12:56
Simple
NEW 20.11.06 12:56 
в ответ rahimov80 20.11.06 12:41
Я не знаю, откуда ты вытащил свое определение для for_each. Вот так она определена в VC6:
template<class _II, class _Fn> inline
_Fn for_each(_II _F, _II _L, _Fn _Op)
{for (; _F != _L; ++_F)
_Op(*_F);
return (_Op); }

Речь идет о третьем параметре - функторе, который передается по значению.

#21 
desyman свой человек20.11.06 15:32
desyman
NEW 20.11.06 15:32 
в ответ rahimov80 20.11.06 12:41
сорри, не догоняю
по-моему будет вызвана Base::foo() в обоих случаях
#22 
Simple Nothing is f*cked20.11.06 15:35
Simple
NEW 20.11.06 15:35 
в ответ desyman 20.11.06 15:32
Ключевое слово: полиморфизм :)
#23 
desyman свой человек20.11.06 16:09
desyman
NEW 20.11.06 16:09 
в ответ Simple 20.11.06 15:35, Последний раз изменено 20.11.06 16:12 (desyman)
в обоих случаях а внутри ф() является обэктом класса Басе
...
или только в первом . че-то торможу
#24 
Murr коренной житель20.11.06 16:28
Murr
NEW 20.11.06 16:28 
в ответ desyman 20.11.06 16:09
ф() - тебя не волнует. Смотри что передается как параметр...
#25 
Simple Nothing is f*cked20.11.06 16:57
Simple
NEW 20.11.06 16:57 
в ответ desyman 20.11.06 16:09
Полиморфизм работает только если параметр передается как указатель или ссылка.
Base *a = new Base;
a->foo(); // вызывается Base::foo
Base *a = new Derived;
a->foo(); // вызывается Derived::foo


#26 
desyman свой человек20.11.06 17:02
desyman
NEW 20.11.06 17:02 
в ответ Simple 20.11.06 16:57
хмм danke schoen
#27 
rahimov80 завсегдатай20.11.06 17:55
NEW 20.11.06 17:55 
в ответ Simple 20.11.06 12:56
Прекрасное определение, у меня такое же
А что будет если задать _Fn как CFactor& ?!
Если мой ответ тебя не удовлетворит, то "поезжайте в Киев и спросите..." (c)
То есть проверь как работает:
for_each<list<CNode>::iterator, CBaseFactor&>(lstCNode.begin(), lstCNode.end(), theFactor)
как theFactor попробуй и CBaseFactor theFactor; и CDerivedFactor theFactor;
Удачи!
#28 
Simple Nothing is f*cked20.11.06 20:20
Simple
NEW 20.11.06 20:20 
в ответ rahimov80 20.11.06 17:55
Да, все правильно.
#29 
voxel3d Убить всех транслитчиков!20.11.06 22:44
voxel3d
NEW 20.11.06 22:44 
в ответ Simple 20.11.06 20:20
Что, "вс╦ правильно"? Для всех элементов коллекции вызовется один и тот же функтор?
Dropbox - средство синхронизации и бэкапа файлов.
#30 
Simple Nothing is f*cked20.11.06 22:48
Simple
NEW 20.11.06 22:48 
в ответ voxel3d 20.11.06 22:44, Последний раз изменено 20.11.06 22:58 (Simple)
Нет, что вызовется правильный, Derived, то бишь. Насчет копирования я не проверял.
зы Проверил, действительно копируется только ссылка.
#31 
voxel3d Убить всех транслитчиков!20.11.06 23:43
voxel3d
NEW 20.11.06 23:43 
в ответ Simple 20.11.06 22:48, Последний раз изменено 20.11.06 23:50 (voxel3d)
Во-первых, не вызовется, потому, что не скомпилируется. Если for_each сделать вида:
  for_each<vector<int>::iterator, BaseFunctor&>

то передать туда как написали выше:

  DerivedFunctor theFunctor;

будет нельзя. Если попытаться сделать нечто типа этого:

  BaseFunctor bf1;  
DerivedFunctor df;
BaseFunctor& bf2 = df;

то попытка вызвать:

  for_each<vector<int>::iterator, BaseFunctor&>(vec.begin(), vec.end(), bf2);

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

Dropbox - средство синхронизации и бэкапа файлов.
#32 
voxel3d Убить всех транслитчиков!21.11.06 00:09
voxel3d
NEW 21.11.06 00:09 
в ответ voxel3d 20.11.06 23:43
И в-третьих, какое вс╦ отношение имеет к процитированному куску из Сартра? Сорри, мне лень искать смотреть, но там, разве, не имеется в виду именно вот это использование for_each:
  class functor ...
for_each(iter_begin, iter_end, functor())

и пишет он про то, что функтор должен быть "л╦гким" для создания копии? Т.к. здесь созда╦тся временный объект-функтор.

Dropbox - средство синхронизации и бэкапа файлов.
#33 
rahimov80 завсегдатай21.11.06 00:10
NEW 21.11.06 00:10 
в ответ voxel3d 20.11.06 23:43
Во, во, я так и говорил: "иди ARM почитай"
В ответ на:
Во-вторых, если бы это работало,
- а у нас что, будет ли код работать или не будет, уже не от стандарта зависит, а от чего-то другого?
Ксати, все ваши примеры прекрасно будут работать: up-casting возможен всегда
Base b = d; Base* pB = pD; Base& b = d; etc.
#34 
voxel3d Убить всех транслитчиков!21.11.06 00:18
voxel3d
NEW 21.11.06 00:18 
в ответ rahimov80 21.11.06 00:10, Последний раз изменено 21.11.06 00:19 (voxel3d)
Пардон, DerivedFunctor унаследовал не от BaseFunctor, а от unary_function<int, void>. Ок, работает, я был неправ, но пункт два это не отменяет - для всех элементов будет вызван один и тот же метод.
Dropbox - средство синхронизации и бэкапа файлов.
#35 
rahimov80 завсегдатай21.11.06 00:58
NEW 21.11.06 00:58 
в ответ Simple 20.11.06 20:20
Ну и замечательно.
Теперь подведем итог, согласно вашей постановке задачи:
В ответ на:
Элементы массива преобразовываются в элементы некоего документа. Операция преобразования может отличаться для разных типов документов и состояния конкретного элемента массива.

1. Если "состояния конкретного элемента массива" можно передать через полиморфизм (т.е. иерархия и т.д.), а тип документа предположим только один, то вариант "Murr/voxel3d": коллекция с ссылками на базовый класс, а функтор не требует полиморфизма, так как определяет документ, который только один.
2. Если "состояния конкретного элемента массива" не требует полиморфизм (может вы имеете в виду разный state в стате-машине внутри элемента ), а разные типы документов определяются через полиморфизм, то вы можете использовать for_each и передавать функтор (определяющий документ) через ссылку.
3. Если вам требуется полиморфизм и для элементов в массиве и для документов, то необходимо воспользоватся паттерном Визитор, как совершенно справедливо заметил scorpi_, так как "double dispatch"!
Ну что,всех примерил?
#36 
Simple Nothing is f*cked21.11.06 08:31
Simple
NEW 21.11.06 08:31 
в ответ voxel3d 20.11.06 23:43
Я же проверил. На GCC 4.0.4 :) Другого компилятора дома не нашлось. Хорошая штука линух :-D
#37 
Simple Nothing is f*cked21.11.06 08:41
Simple
NEW 21.11.06 08:41 
в ответ rahimov80 21.11.06 00:58
В конечном итоге у меня получилось, что полиморфизм не требуется ни на одной стороне, но я все равно сделал по п. 3, потому что будет проще расширить,если что-то изменится. К тому же, функторы рекомендуется передавать по значению - не понял, почему, но верю Саттеру :)
> Ну что,всех примерил?
Примеряют штаны :-P
#38 
Simple Nothing is f*cked21.11.06 08:45
Simple
NEW 21.11.06 08:45 
в ответ voxel3d 21.11.06 00:18
Ты имеешь в виду, что п-м не работает? Так работает же.
#ifndef __TEST_CPP__
#define __TEST_CPP__
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;
class Base : public unary_function<int, void>
{
public:
Base() {}
Base(const Base& base)
{
n_copied++;
}
void operator()(int num)
{
cout << num_op(num) << " ";
}
Base& operator=(const Base& base)
{
n_assigned++;
}
static int n_copied;
static int n_assigned;
private:
virtual int num_op(int num)
{
return num;
}
};
int Base::n_copied = 0;
int Base::n_assigned = 0;
class Derived : public Base
{
public:
Derived() {}
Derived(const Derived& d)
{
n_copied++;
}
private:
virtual int num_op(int num)
{
return num*num;
}
};
int main(int argc, char* argv[])
{
vector<int> coll;
for (int i = 0; i < 10; i++)
coll.push_back(i);
Base b;
Derived d;
cout << "n_copied = " << Base::n_copied << endl;
for_each<vector<int>::iterator, Base&>(coll.begin(), coll.end(), b);
cout << endl;
cout << "n_copied = " << Base::n_copied << endl;
for_each<vector<int>::iterator, Base&>(coll.begin(), coll.end(), d);
cout << endl;
cout << "n_copied = " << Derived::n_copied << endl;
return 0;
}
#endif


#39 
rahimov80 завсегдатай21.11.06 12:04
NEW 21.11.06 12:04 
в ответ Simple 21.11.06 08:41, Последний раз изменено 21.11.06 12:09 (rahimov80)
Совершенно справедливое решение: думать об возможности рассширения - это хороший стиль!
Но и в случае Visitor, ты в CElement::Accept() сам Visitor == Functor должен передавать по ссылке.
Как там было: "иди GoF почитай"
В ответ на:
> Ну что,всех примерил?
Примеряют штаны :-P
Вот проблема, как "легашер" пишется ещё помню לגשר, а вот в русском уже делаю ошибки (לגשר-to reconcile)
#40 
Simple Nothing is f*cked21.11.06 12:06
Simple
NEW 21.11.06 12:06 
в ответ rahimov80 21.11.06 12:04
В ответ на:
Но и в случае Visitor, ты в CElement::Accept() сам Visitor == Functor должен передавать по ссылке.

Естественно.
В ответ на:
Вот проблема, как "легашер" пишется ещё помню לגשר, а вот в русском уже делаю ошибки

Проверочное слово - мир :) Не страшно, никто не безгрешен.
#41 
rahimov80 завсегдатай21.11.06 12:13
NEW 21.11.06 12:13 
в ответ Simple 21.11.06 12:06
В ответ на:
Проверочное слово - мир :)
или мера, мерить и т.д.
#42 
1 2 3 все