General Functors

642 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
642
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

General Functors

  1. 1. Generalized FUNCTORS В рамках курсу “Актуальні проблеми програмної інженерії”, 2009 р. Лозинський Ігор, Фін-3
  2. 2. Ги-ги… <ul><li>Додаткова інформація: </li></ul><ul><li>В презентації 32 сторінки </li></ul><ul><li> Наперед перепрошую за всі лажі та недопрацювання. </li></ul><ul><li> </li></ul>
  3. 3. Майже гасло <ul><li>“ Особливо розумні” технології призначені для досягнення простоти. </li></ul><ul><li>( original : Clever techniques should be applied for the benefit of simplicity. ) </li></ul>
  4. 4. Що воно таке? <ul><li>Узагальнений функтор – це будь-який виклик процедури, що дозволений в С++ та інкапсульований в об'єкт першого класу, який гарантує типову безпеку </li></ul><ul><li>generalized functor is any processing invocation that C++ allows, encapsulated as a typesafe first-class object </li></ul>
  5. 5. Для чого? <ul><li>Узагальнені функтори дозволяють зберігати виклики процедур у вигляді значень, передавати їх в якості параметрів, і виконувати іх далеко від місця створення. Суттєвою відмінністю між вказівниками на функції та узагальненими функторами в тому, що останні можуть зберігати стан об'єкта та викликати його методи. </li></ul>
  6. 6. The Command Design Pattern <ul><li>Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides . 1995. Design Patterns: Elements of Reusable Object-Oriented Software. </li></ul>
  7. 7. Що це дає? <ul><li>Модуль, що здійснює виклик не тільки не знає як виконується робота, а й не має уявлення для якого виду роботи призначений клас Command </li></ul>
  8. 8. Застосування <ul><li>// Resize the window </li></ul><ul><li>window.Resize(0, 0, 200, 100); </li></ul><ul><li>Command resizeCmd( </li></ul><ul><li> window, // Object </li></ul><ul><li> &Window::Resize, // Member function </li></ul><ul><li> 0, 0, 200, 100); // Arguments </li></ul><ul><li>// Later on... </li></ul><ul><li>resizeCmd.Execute(); // Resize the window </li></ul>
  9. 9. В реальному житті <ul><li>Розробка інтерфейсів користувача окремо від самих програм ( skinnable ) </li></ul><ul><li>Оболонки не мають архітектури, вони лише надають місця для об'єктів класу Command </li></ul>
  10. 10. Generalized callback <ul><li>void Foo(); void Bar(); </li></ul><ul><li>int main() </li></ul><ul><li>{ </li></ul><ul><li>void (*pF)() = &Foo; </li></ul><ul><li>Foo(); // Call Foo directly </li></ul><ul><li>Bar(); // Call Bar directly </li></ul><ul><li>(*pF)(); // Call Foo via pF </li></ul><ul><li>void (*pF2)() = pF; // Create a copy of pF </li></ul><ul><li>pF = &Bar; // Change pF to point to Bar </li></ul><ul><li>(*pF)(); // Now call Bar via pF </li></ul><ul><li>(*pF2)(); // Call Foo via pF2 </li></ul><ul><li>} </li></ul>
  11. 11. Ті, що підтримують operator() ( callable ) <ul><li>Функції </li></ul><ul><li>Вказівники на функції </li></ul><ul><li>Відсилки на функції (константні вказівники) </li></ul><ul><li>Функтори (об'єкти, в яких визначений operator() ) </li></ul><ul><li>Результати застосування операторів .* та ->* до указників на функції-члени. </li></ul>
  12. 12. Скелет Functor ’ а <ul><li>Клас Functor має поліморфну реалізацію, але це сховано всередині нього. </li></ul><ul><li>Реалізація базового класу - FunctorImpl </li></ul>
  13. 13. Починаємо реалізовувати <ul><li>Перша реалізація – перша проблема </li></ul><ul><li>class Functor { </li></ul><ul><li>public: void operator ()(); </li></ul><ul><li>// other member functions </li></ul><ul><li>private: </li></ul><ul><li>// implementation goes here </li></ul><ul><li>}; </li></ul>
  14. 14. Виходить без шаблонів ніяк? <ul><li>template < typename ResultType> </li></ul><ul><li>class Functor { </li></ul><ul><li>public: </li></ul><ul><li>ResultType operator ()(); </li></ul><ul><li>// other member functions </li></ul><ul><li>private: </li></ul><ul><li>// implementation </li></ul><ul><li>}; </li></ul>
  15. 15. Ну а де аргументи? <ul><li>Ми не маємо морального права накладати обмеження на кількість аргументів operator() ’ а та на їх тип. </li></ul><ul><li>Крім того змінних шаблонів в С++ немає. </li></ul><ul><li>А еліпси ( ellipsis ) типу printf – це не красиво, та ще й небезпечно. </li></ul><ul><li>Що робити? </li></ul>
  16. 16. Так теж не годиться <ul><li>// Functor with no arguments template </li></ul><ul><li>< typename ResultType> </li></ul><ul><li>class Functor { ... }; </li></ul><ul><li>// Functor with one argument template </li></ul><ul><li>< typename ResultType, typename Parm1> </li></ul><ul><li>class Functor { ... }; </li></ul>
  17. 17. Списки типів  <ul><li>< typename ResultType, class TList> </li></ul><ul><li>class Functor { ... }; </li></ul><ul><li>Functor <double, TYPELIST_2 (int, double)> m yFunctor; </li></ul>
  18. 18. Але й списки не ідеальні <ul><li>template < typename R> </li></ul><ul><li>class FunctorImpl<R, NullType> { </li></ul><ul><li>public: virtual R operator() () = 0; </li></ul><ul><li>virtual FunctorImpl* Clone() const = 0; </li></ul><ul><li>virtual ~FunctorImpl() {} </li></ul><ul><li>}; </li></ul><ul><li>template < typename R, typename P1> </li></ul><ul><li>class FunctorImpl<R, TYPELIST_1(P1)>{ </li></ul><ul><li>public: virtual R operator() (P1) = 0; </li></ul><ul><li>virtual FunctorImpl* Clone() const = 0; </li></ul><ul><li>virtual ~FunctorImpl() {} </li></ul><ul><li>}; </li></ul>
  19. 19. Сам пан Functor <ul><li>template < typename R, class TList> </li></ul><ul><li>class Functor { </li></ul><ul><li>public: </li></ul><ul><li>Functor(); </li></ul><ul><li>Functor( const Functor&); </li></ul><ul><li>Functor& operator=( const Functor&); </li></ul><ul><li>explicit Functor( std::auto_ptr <Impl> spImpl); </li></ul><ul><li>... </li></ul><ul><li>private: </li></ul><ul><li>FunctorImpl<R, TList> Impl; </li></ul><ul><li>std::auto_ptr <Impl> spImpl_; </li></ul><ul><li>}; </li></ul>
  20. 20. Маленька хитрість <ul><li>template < typename R, class TList> </li></ul><ul><li>class Functor { </li></ul><ul><li>typedef TList ParmList; </li></ul><ul><li>typedef typename TypeAtNonStrict<TList, 0, EmptyType>::Result Parm1; </li></ul><ul><li>typedef typename TypeAtNonStrict<TList, 1, EmptyType>::Result Parm2; </li></ul><ul><li>... as above ... </li></ul><ul><li>}; </li></ul><ul><li>Доступаємось до типу, знаючи його номер (дивно, але працює  ) </li></ul>
  21. 21. Реалізація operator() ’ а <ul><li>template < typename R, class TList> </li></ul><ul><li>class Functor { </li></ul><ul><li>... as above ... </li></ul><ul><li>public: </li></ul><ul><li>R operator() () </li></ul><ul><li>{ return (*spImpl_)(); } </li></ul><ul><li>R operator() (Parm1 p1) </li></ul><ul><li>{ return (*spImpl_)(p1); } </li></ul><ul><li>R operator() (Parm1 p1, Parm2 p2) </li></ul><ul><li>{ return (*spImpl_)(p1, p2); } </li></ul><ul><li>}; </li></ul>
  22. 22. В чому фокус? <ul><li>Functor<double, TYPELIST_2(int, double)> myFunctor; </li></ul><ul><li>double result = myFunctor(4, 5.6); </li></ul><ul><li>// Wrong invocation. </li></ul><ul><li>double result = myFunctor(); </li></ul>
  23. 23. Робота з функторами <ul><li>template < typename R, class TList> </li></ul><ul><li>class Functor { </li></ul><ul><li>... as above ... </li></ul><ul><li>public : </li></ul><ul><li>template < class Fun> </li></ul><ul><li>Functor( const Fun& fun); </li></ul><ul><li>}; </li></ul><ul><li>Таким чином ми доступатимемось до інших функторів </li></ul>
  24. 24. Так багато коду для одного конструктора <ul><li>template < class ParentFunctor, typename Fun> </li></ul><ul><li>class FunctorHandler : public FunctorImpl </li></ul><ul><li> < </li></ul><ul><li> typename ParentFunctor::ResultType, </li></ul><ul><li> typename ParentFunctor::ParmList </li></ul><ul><li> > </li></ul><ul><li>{ </li></ul><ul><li>public : </li></ul><ul><li>typedef typename ParentFunctor::ResultType ResultType; </li></ul><ul><li>… </li></ul>
  25. 25. … готуємо конструктор <ul><li>FunctorHandler( const Fun& fun) : fun_(fun) {} </li></ul><ul><li>FunctorHandler* Clone() const { </li></ul><ul><li>return new FunctorHandler(*this); } </li></ul><ul><li>ResultType operator() () { return fun_(); } </li></ul><ul><li>ResultType operator() ( typename ParentFunctor::Parm1 p1) { </li></ul><ul><li>return fun_(p1); } </li></ul><ul><li>ResultType operator() ( typename ParentFunctor::Parm1 p1, typename ParentFunctor::Parm2 p2){ </li></ul><ul><li>return fun_(p1, p2); } </li></ul><ul><li>private: </li></ul><ul><li>Fun fun_ ; </li></ul><ul><li>}; </li></ul>
  26. 26. Нарешті вимучили наш конструктор <ul><li>template < typename R, class TList> </li></ul><ul><li>template < typename Fun> </li></ul><ul><li>Functor<R, TList>::Functor( const Fun& fun) : </li></ul><ul><li>spImpl_( new FunctorHandler<Functor, Fun>(fun)); </li></ul><ul><li>{ } </li></ul><ul><li>Ще один трюк – шаблонне визначення члена поза межами класу </li></ul><ul><li>( &quot;out-of-class member template definition.&quot; ) </li></ul>
  27. 27. Тестуємо – все красиво <ul><li>#include “ Functor.h ” </li></ul><ul><li>#include <iostream> </li></ul><ul><li>struct TestFunctor { </li></ul><ul><li>void operator() (int i, double d) { </li></ul><ul><li>cout <<&quot;TestFunctor::operator()(&quot; << i << &quot;, &quot; << d << &quot;) called. &quot;; } </li></ul><ul><li>}; </li></ul><ul><li>int main() { </li></ul><ul><li>TestFunctor f; </li></ul><ul><li>Functor<void, TYPELIST_2(int, double)> cmd(f); </li></ul><ul><li>cmd(4, 4.5); </li></ul><ul><li>} </li></ul>
  28. 28. Не просто красиво – дуже красиво <ul><li>Працюємо з функторами </li></ul><ul><li>Працюємо з функціями </li></ul><ul><li>Автоматично зводимо типи аргументів та результатів (все й справді чесно) (приклад: char* -> string ) </li></ul>
  29. 29. Вказівники на методи об'єктів <ul><li>Є певні нюанси (але вони дещо виходять за межі нашого обговорення, та й для чого ускладнювати?) </li></ul><ul><li>Реалізується тим же шляхом що й FunctorHandler </li></ul>
  30. 30. Бонуси <ul><li>Зв'язування певних атрибутів (до початку виклику ми не тільки вкажемо на виконавця, але й задамо атрибути виконання, тобто певне описання середовища, в якому відбувається робота ). </li></ul><ul><li>Ланцюжок – ( MacroCommand , з Gamma et al* , 1995 ) - дозволяє створювати пакет виконання команд. Всі команди на момент “запаковування” повинні бути зв'язаними. Проте пізніше однією командою можна запустити цілу програму, створену на етапі виконання. </li></ul><ul><li>* Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides . 1995. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley. </li></ul>
  31. 31. Де це знайти? <ul><li>loki </li></ul><ul><li>Посилання: </li></ul><ul><li>http://sourceforge.net/projects/loki-lib </li></ul><ul><li>Для презентації використовувалась версія 0.1.7 </li></ul>
  32. 32. Ніби кінець… <ul><li>Спасибі за увагу </li></ul><ul><li>Буду вдячний, якщо питання Ви не задаватимете.  </li></ul>

×