Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
bind [2008/04/12 19:24] maciejp |
bind [2008/04/14 08:58] maciejp |
||
---|---|---|---|
Linia 1: | Linia 1: | ||
======Biblioteka Boost.Bind====== | ======Biblioteka Boost.Bind====== | ||
- | Funkcja boost::bind jest generalizacją funkcji std::bind1st i std::bind2nd dostępnych w bibliotece standardowej. Funkcja ta wspiera funktory, funkcje, wskaźniki do funkcji oraz wskaźniki do funkcji składowych. Jest w stanie związać dowolny argument ze specyficzną wartością albo przekierować argumenty wejściowe we wskazane pozycje. boost::bind nie nakłada żadnych wymagań obiektom funkcyjnym, w ogólności nie wymaga standardowych definicji typów takich jak: result_type, first_argument_type, second_argument_type. | + | Funkcja ''boost::bind'' jest generalizacją funkcji ''std::bind1st'' i ''std::bind2nd'' dostępnych w bibliotece standardowej. Funkcja ta wspiera funktory, funkcje, wskaźniki do funkcji oraz wskaźniki do funkcji składowych. Jest w stanie związać dowolny argument ze specyficzną wartością albo przekierować argumenty wejściowe we wskazane pozycje. ''boost::bind'' nie nakłada żadnych wymagań obiektom funkcyjnym, w ogólności nie wymaga standardowych definicji typów takich jak: ''result_type'', ''first_argument_type'', ''second_argument_type''. |
- | Do korzystania z funcji boost::bind konieczne jest dołączenie nagłówka “boost/bind.hpp”. | + | Do korzystania z funcji boost::bind konieczne jest dołączenie nagłówka ''boost/bind.hpp''. |
- | + | ||
Linia 24: | Linia 22: | ||
</code> | </code> | ||
- | Najprostszym przypadkiem użycia boost::bind jest stworzenie bezargumentowego funktora przez zastąpienie argumentów funkcji wartościami stałymi. | + | Najprostszym przypadkiem użycia ''boost::bind'' jest stworzenie bezargumentowego funktora przez zastąpienie argumentów funkcji wartościami stałymi. |
<code cpp> | <code cpp> | ||
Linia 30: | Linia 28: | ||
</code> | </code> | ||
- | Jako pierwszy argument boost::bind podajemy wskaźnik do funkcji, następne argumenty przypisywane są jako pierwszy i drugi argument funkcji g. Typ zwracany przez funktor jest identyczny z typem zwracanym przez funkcję podaną jako pierwszy argument. | + | Jako pierwszy argument ''boost::bind'' podajemy wskaźnik do funkcji, następne dwa przypisywane są jako pierwszy i drugi argument funkcji ''g''. Typ zwracany przez funktor jest identyczny z typem zwracanym przez funkcję podaną jako pierwszy argument. |
Znacznie ciekawszym mechanizmem jest możliwość selektywnego zastępowania argumentów lub kolejności przekazywanych argumentów. | Znacznie ciekawszym mechanizmem jest możliwość selektywnego zastępowania argumentów lub kolejności przekazywanych argumentów. | ||
Linia 36: | Linia 34: | ||
int main() { | int main() { | ||
int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9; | int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9; | ||
- | (boost::bind(&nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7)) //utworzenie tymczasowego funktora i wywołanie | + | boost::bind(f,_9,_2,_1,_6,_3,_8,_4,_5,_7) //utworzenie tymczasowego funktora i wywołanie |
- | (i1,i2,i3,i4,i5,i6,i7,i8,i9); //odpowiadające wywołaniu f(i9, i2, i1, i6, i3, i8, i4, i5, i7) | + | (i1,i2,i3,i4,i5,i6,i7,i8,i9); //odpowiadające wywołaniu f(i9, i2, i1, i6, i3, i8, i4, i5, i7) |
- | //w wyniku otrzymamy ciąg: 921638457 | + | //w wyniku otrzymamy ciąg: 921638457 |
- | } | + | return 0; |
- | bind(f, _2, _3, _1, _1, 0, 0, _3, 0, _2)(x, y, z); //utworzenie tymczasowego funktora i wywołanie | + | } |
- | + | ||
</code> | </code> | ||
- | Realizujemy to za pomocą symbli zastępczych oznaczanych jako _1, _2, ..., _9. Dostępnych mamy łacznie 9 takich symboli. Ogranicza to możliwośc tworzenia funktorów do funktorów 9 elementowych (w przypadku funkcji składowych jest to 8 argumentów; musimy pamietć o przekazaniu obiektu na rzecz, którego będzie wywoływany funktor). Symbol _1 oznacza "zastąp przez pierwszy argument wejściowy", analogicznie pozostałe osiem. | + | Realizujemy to za pomocą symbli zastępczych oznaczanych jako ''_1'', ''_2'', ..., ''_9''. Dostępnych mamy łacznie 9 takich symboli. Ogranicza to możliwośc tworzenia funktorów do funktorów 9 elementowych (w przypadku funkcji składowych jest to 8 argumentów; musimy pamietć o przekazaniu obiektu na rzecz, którego będzie wywoływany funktor). Symbol ''_1'' oznacza "zastąp przez pierwszy argument wejściowy", analogicznie pozostałe osiem. |
- | Jak wynika z przykładu, można dowolnie "mieszać" argumenty ze stałymi jak i ich kolejność. | + | Argumenty można dowolnie zamieniać ze stałymi jak i zamieniać ich kolejność. |
+ | |||
=====Użycie z funktorami===== | =====Użycie z funktorami===== | ||
- | boost::bind nie jest ograniczony tylko do funkcji, akceptuje min. również funktory. W przypadku użycia funktora zazwyczaj trzeba jawnie podać typ zwracany przez operator() funktora (ewentualnie zdefinjować typ result_type w funktorze). | + | ''boost::bind'' nie jest ograniczony tylko do wskaźników do funkcji, akceptuje min. również funktory. W przypadku użycia funktora zazwyczaj należy jawnie podać typ zwracany przez ''operator()'' funktora (ewentualnie zdefinjować typ ''result_type'' w funktorze). |
<code cpp> | <code cpp> | ||
Linia 65: | Linia 64: | ||
</code> | </code> | ||
- | Niektóre kompilatory mają problem ze składnią w postaci bind<R>(...). Wyrażenie to można zastąpić postacią alternatwną. | + | Niektóre kompilatory mają problem ze składnią w postaci ''bind<R>(...)''. Wyrażenie to można zastąpić postacią alternatwną. |
<code cpp> | <code cpp> | ||
boost::bind(boost::type<int>(), f, _1, _1)(x); | boost::bind(boost::type<int>(), f, _1, _1)(x); | ||
</code> | </code> | ||
+ | |||
+ | |||
+ | |||
=====Użycie ze wskaźnikami do składowych===== | =====Użycie ze wskaźnikami do składowych===== | ||
- | Wskaźniki do metod i danych sładowych nie są obiektami funkcyjnymi, ponieważ nie wspierają operatora operator(). Dla wygody boost::bind akceptuje te wskażniki jako pierwszy argument. boost::bind używa funkcji boost::mem_fn do przekonwertowania wskźników do składowych na funktory. Innymi słowy, wyrażenie: | + | Wskaźniki do metod i danych sładowych nie są obiektami funkcyjnymi, ponieważ nie wspierają operatora ''operator()''. Dla wygody ''boost::bind'' akceptuje te wskażniki jako pierwszy argument. ''boost::bind'' używa funkcji ''boost::mem_fn'' do konwersji wskźników do składowych na funktory. Innymi słowami, wyrażenie: |
<code cpp> | <code cpp> | ||
Linia 86: | Linia 88: | ||
</code> | </code> | ||
- | gdzie R jest typem zwracanym przez Foo::f w przypadku metod lub typem danych w przypadku danych składowych. | + | gdzie ''R'' jest typem zwracanym przez ''Foo::f'' w przypadku metod lub typem danych w przypadku danych składowych. |
Przykład: | Przykład: | ||
Linia 106: | Linia 108: | ||
</code> | </code> | ||
- | Jako drugi argument boost::bind przekazujemy obiekt klasy na rzecz, której ma być wywoływana funkcja (jest to oczywiste, jeśli zauważymy, że pierwszym niejawnym argumentem metod jest zawsze wskaźnik this - adres obiektu). Najciekawsze są dwie ostatnie linijki przykładu. bind(&Foo::f, x, _1) przechowuje wewnętrzną kopję obieku x (warto zwrócić na to uwagę, szczególnie gdy kopjowanie obiektu jest kosztowne), natomiast bind(&Foo::f, p, _1) kopję wskażnika p (p jest sprytnym wskaźnikiem shared_ptr<>), tworzony obiekt funkcyjny przechowuje swoją referencję do instancji klasy Foo i będzie ona poprawna nawet kiedy p wyjdzie poza zasięg lub wykonamy na p reset(). | + | Jako drugi argument ''boost::bind'' przekazujemy obiekt klasy na rzecz, której ma być wywoływana funkcja (jest to oczywiste, jeśli zauważymy, że pierwszym niejawnym argumentem metod jest zawsze wskaźnik this - adres obiektu). Najciekawsze są dwie ostatnie linijki przykładu. ''bind(&Foo::f, x, _1)'' przechowuje wewnętrzną kopję obieku ''x'' (warto zwrócić na to uwagę, szczególnie gdy kopjowanie obiektu jest kosztowne), natomiast ''bind(&Foo::f, p, _1)'' kopję wskażnika ''p'' (''p'' jest sprytnym wskaźnikiem ''shared_ptr<>''), tworzony obiekt funkcyjny przechowuje swoją referencję do instancji klasy Foo i będzie ona poprawna nawet kiedy p wyjdzie poza zasięg lub wykonamy na ''p reset()''. |
- | Funcję boost::bind można też bezprolemowo użyć z funkcjami wirtualnymi. | + | Funcję ''boost::bind'' można też bezprolemowo użyć z funkcjami wirtualnymi. |
<code cpp> | <code cpp> | ||
Linia 131: | Linia 133: | ||
base* bp = &d; | base* bp = &d; | ||
- | boost::bind(&base::print, _1)(b); | + | boost::bind(&base::print, _1)(b); |
- | boost::bind(&base::print, _1)(bp); | + | boost::bind(&base::print, _1)(bp); |
- | boost::bind(&base::print, _1)(d); | + | boost::bind(&base::print, _1)(d); |
+ | |||
+ | return 0; | ||
+ | } | ||
</code> | </code> | ||
Linia 143: | Linia 148: | ||
derived! | derived! | ||
</code> | </code> | ||
+ | |||
=====Użycie zagnieżdżeń do kompozycji fukcji===== | =====Użycie zagnieżdżeń do kompozycji fukcji===== | ||
- | Część argumentów przekazywanych bind może być zagnieżdżonymi wyrażeniami boost::bind. | + | Część argumentów przekazywanych bind może być zagnieżdżonymi wyrażeniami ''boost::bind''. |
<code cpp> | <code cpp> | ||
Linia 153: | Linia 159: | ||
</code> | </code> | ||
- | Wewnętrzne wyrażenia są ewaluowane w bliżej nie okreśłonej kolejności, ale przed wywołaniem funktora zewnętzrnego boost::bind. Odnosząc się do przykładu, kiedy utworzony funktor jest wywoływany z argumentem x, w pierwszej kolejności ewalowany jest bind(g, _1)(x), dając g(x), następnie bind(f, g(x))(x), dając finalny wynik w postaci wywołania f(g(x)). | + | Wewnętrzne wyrażenia są ewaluowane w bliżej nie okreśłonej kolejności, ale przed wywołaniem funktora zewnętzrnego ''boost::bind''. Odnosząc się do przykładu, kiedy utworzony funktor jest wywoływany z argumentem ''x'', w pierwszej kolejności ewalowany jest ''bind(g, _1)(x)'', dając ''g(x)'', następnie ''bind(f, g(x))(x)'', dając finalny wynik w postaci wywołania ''f(g(x))''. |
- | Ta cecha boost::bind może być bardzo dobrze zastosowana do kompozycji funkcji. | + | Ta cecha ''boost::bind'' może być bardzo dobrze zastosowana do kompozycji funkcji. |
- | Trzeba zaznaczyć, że pierwszy argument boost::bind (opakowywany obiekt funkcyjny) nie jest ewalowany, nawet wtedy, kiedy jest to obiekt funkcyjny produkowany przez zagnieżdżone wywołanie bind lub argument w postaci symbola zastępczego. Poniższy przykład nie będzie działał tak, jakbyśmy tego oczekiwali. | + | Trzeba zaznaczyć, że pierwszy argument ''boost::bind'' (opakowywany obiekt funkcyjny) nie jest ewalowany, nawet wtedy, kiedy jest to obiekt funkcyjny produkowany przez zagnieżdżone wywołanie ''bind'' lub argument w postaci symbola zastępczego. Poniższy przykład nie będzie działał tak, jakbyśmy tego oczekiwali. |
<code cpp> | <code cpp> | ||
Linia 167: | Linia 173: | ||
</code> | </code> | ||
- | Oczekiwany efekt uzyskamy korzystając z pomocniczego obiektu funkcyjnego apply (implementacja apply w boost/bind/apply.hpp), który podajemy jako pierwszy argument boost::bind. Poprzedni przykład powinien wyglądać następująco. | + | Oczekiwany efekt uzyskamy korzystając z pomocniczego obiektu funkcyjnego ''apply'' (implementacja ''apply'' w ''boost/bind/apply.hpp''), który podajemy jako pierwszy argument ''boost::bind''. Poprzedni przykład powinien wyglądać następująco. |
<code cpp> | <code cpp> | ||
Linia 177: | Linia 183: | ||
</code> | </code> | ||
- | Używając bind można komponować funkcje korzystając naraz z boost::bind i funkcji (algorytmów) z biblioteki standardowej. | + | Używając ''bind'' można komponować funkcje korzystając naraz z ''boost::bind'' i funkcji (algorytmów) z biblioteki standardowej. |
<code cpp> | <code cpp> | ||
Linia 209: | Linia 215: | ||
</code> | </code> | ||
- | Jak widać na przykładzie łatwo można tworzyć złożone predykaty. Często trzeba się jednak często zastanowić, czy stosować bardzo złożone wyrażenia. Utrudniają one późniejszą analizę kodu, często wręcz uniemożliwiają. Warto rozważyć w takich przypadkach zdefinjowanie funktora lub operatora operator() dla klasy. | + | Jak widać na przykładzie łatwo można tworzyć złożone predykaty. Często trzeba się jednak często zastanowić, czy stosować bardzo złożone wyrażenia. Utrudniają one późniejszą analizę kodu, często wręcz uniemożliwiają. Warto rozważyć w takich przypadkach zdefinjowanie funktora lub operatora ''operator()'' dla klasy. |
=====Użycie z biblioteką Boost.Function===== | =====Użycie z biblioteką Boost.Function===== | ||
- | Funktor zwracany przez boost::bind można przypisać do obiektu funkcyjnego boos::function. W ten sposób można mięzy innymi przechowywć wcześniej utworzone fukntory lub przekazywać je jako argument do konstruktorów, funkcji. Przypisania możemy dokonać wykłym operatorem =. | + | Funktor zwracany przez ''boost::bind'' można przypisać do obiektu funkcyjnego ''boos::function''. W ten sposób można między innymi przechowywć wcześniej utworzone fukntory jako zmienne i pola klasy albo przekazywać je jako argument do konstruktorów lub funkcji. Przypisania możemy dokonać wykłym operatorem ''=''. |
<code cpp> | <code cpp> | ||
Linia 221: | Linia 228: | ||
</code> | </code> | ||
- | Warunkiem poprawnej kompilacji jest dokładne określenie typu szablonu obiektu function (zasada działania boost::function wykracza poza ten artykuł). | + | Warunkiem poprawnej kompilacji jest dokładne określenie typu szablonu obiektu function (zasada działania ''boost::function'' wykracza poza ten artykuł). |
=====Przeciążone operatory===== | =====Przeciążone operatory===== | ||
- | Przeciązone operatory dla boost::bind pojawiły się w Boost 1.33 | + | Przeciązone operatory dla ''boost::bind'' pojawiły się w Boost 1.33 |
- | Obiekty funkcyjne produkowane przez boost::bind przeciążają logiczny operator przecenia ! oraz operatory relacji ==, !=, <, <=, >, >=. | + | Obiekty funkcyjne produkowane przez ''boost::bind'' przeciążają logiczny operator przecenia ''!'' oraz operatory relacji ''=='', ''!='', ''<'', ''<='', ''>'', ''>=''. |
<code> | <code> | ||
!bind(f, ...) | !bind(f, ...) | ||
</code> | </code> | ||
+ | |||
Jest ekwiwalentem | Jest ekwiwalentem | ||
+ | |||
<code cpp> | <code cpp> | ||
bind(logical_not(), bind(f, ...)) | bind(logical_not(), bind(f, ...)) | ||
</code> | </code> | ||
- | gdzie logical_not jest funktorem przyjmującym jeden argument x i zwracający !x. | + | |
+ | gdzie ''logical_not'' jest funktorem przyjmującym jeden argument ''x'' i zwracający ''!x''. | ||
<code cpp> | <code cpp> | ||
bind(f, ...) op x | bind(f, ...) op x | ||
- | <code> | + | </code> |
- | op jest operatorem relacji, wyrażniu temu odpowiada | + | ''op'' jest operatorem relacji, wyrażniu temu odpowiada |
<code cpp> | <code cpp> | ||
bind(relation(), bind(f, ...), x) | bind(relation(), bind(f, ...), x) | ||
</code> | </code> | ||
- | gdzie relation jest funktorem przyjmującym dwa argumenty a i b i zwracającym a op b. | + | gdzie ''relation'' jest funktorem przyjmującym dwa argumenty ''a'' i ''b'' oraz zwracającym ''a op b''. |
- | Przeciążenie tych operatorów umożliwia na konwencjonalne negowanie wyniku boost::bind: | + | Przeciążenie tych operatorów umożliwia na konwencjonalne negowanie wyniku ''boost::bind'': |
<code cpp> | <code cpp> | ||
Linia 255: | Linia 265: | ||
</code> | </code> | ||
- | oraz na porównywanie wyników boost::bind z wartościami: | + | oraz na porównywanie wyników ''boost::bind'' z wartościami: |
<code cpp> | <code cpp> | ||
Linia 267: | Linia 277: | ||
</code> | </code> | ||
- | albo z innym wyrażeniem boost::bind: | + | albo z innym wyrażeniem ''boost::bind'': |
<code cpp> | <code cpp> | ||
Linia 274: | Linia 284: | ||
- | =====Przykład użycia===== | + | |
+ | =====Inny przykład użycia===== | ||
boost::bind umożliwia w przeciwnieństwie do funkcji z biblioteki standardowej (służących do tworzenia adpterów funkcji) bardzo elastyczną pracę z kodem. | boost::bind umożliwia w przeciwnieństwie do funkcji z biblioteki standardowej (służących do tworzenia adpterów funkcji) bardzo elastyczną pracę z kodem. | ||
Linia 317: | Linia 328: | ||
statuses.end(), | statuses.end(), | ||
boost::bind(&status::report, _1)); | boost::bind(&status::report, _1)); | ||
+ | |||
+ | return 0; | ||
} | } | ||
</code> | </code> |