przejście do zawartości
zpr c++ quick reference
Narzędzia użytkownika
Zarejestruj się!
Zaloguj
Narzędzia witryny
Narzędzia
Pokaż stronę
Poprzednie wersje
Odnośniki
Ostatnie zmiany
Menadżer multimediów
Indeks
Zaloguj
Zarejestruj się!
Ostatnie zmiany
Menadżer multimediów
Indeks
Ślad:
bind
Ta strona jest tylko do odczytu. Możesz wyświetlić źródła tej strony ale nie możesz ich zmienić.
======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. Do korzystania z funcji boost::bind konieczne jest dołączenie nagłówka “boost/bind.hpp”. =====Użycie z funkcjami i wskaźnikami do funkcji===== Zacznijmy od stworzenia dwóch prostych funkcji. <code cpp> int g(int a, int b) { return a + b; } void f(int i1,int i2,int i3,int i4, int i5,int i6,int i7,int i8, int i9) { std::cout << i1 << i2 << i3 << i4 << i5 << i6 << i7 << i8 << i9 << '\n'; } </code> Najprostszym przypadkiem użycia boost::bind jest stworzenie bezargumentowego funktora przez zastąpienie argumentów funkcji wartościami stałymi. <code cpp> bind(g, 1, 2); //utworzony tutaj funktor ze stałymi arumentami odpowiada g(1, 2); </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. Znacznie ciekawszym mechanizmem jest możliwość selektywnego zastępowania argumentów lub kolejności przekazywanych argumentów. <code cpp> int main() { int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9; 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) //w wyniku otrzymamy ciąg: 921638457 return 0; } </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. Argumenty można dowolnie zamieniać ze stałymi jak i zamieniać ich kolejność. =====Użycie z funktorami===== 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> struct Foo { int operator()(int a, int b) { return a - b; } bool operator()(long a, long b) { return a == b; } }; Foo f; int x = 1024; bind<int>(f, _1, _1)(x); //wywołanie f(x, x), wynik 0 </code> Niektóre kompilatory mają problem ze składnią w postaci bind<R>(...). Wyrażenie to można zastąpić postacią alternatwną. <code cpp> boost::bind(boost::type<int>(), f, _1, _1)(x); </code> =====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: <code cpp> bind(&Foo::f, args) </code> odpowiada <code cpp> bind<R>(mem_fn(&Foo::f), args) </code> gdzie R jest typem zwracanym przez Foo::f w przypadku metod lub typem danych w przypadku danych składowych. Przykład: <code cpp> struct Foo { bool f(int a); }; Foo x; shared_ptr<Foo> p(new Foo); int i = 5; bind(&Foo::f, boost::ref(x), _1)(i); // x.f(i) bind(&Foo::f, &x, _1)(i); //(&x)->f(i) bind(&Foo::f, x, _1)(i); //(wewnętrzna kopja x).f(i) bind(&Foo::f, p, _1)(i); //(wewnętrzna kopja p)->f(i) </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(). Funcję boost::bind można też bezprolemowo użyć z funkcjami wirtualnymi. <code cpp> class base { public: virtual void print() const { std::cout << "base!\n"; } virtual ~base() {} }; class derived : public base { public: void print() const { std::cout << "derived!\n"; } }; int main() { derived d; base b; base* bp = &d; boost::bind(&base::print, _1)(b); boost::bind(&base::print, _1)(bp); boost::bind(&base::print, _1)(d); return 0; } </code> Wynikiem będzie oczekiwany ciąg: <code cpp> base! derived! derived! </code> =====Użycie zagnieżdżeń do kompozycji fukcji===== Część argumentów przekazywanych bind może być zagnieżdżonymi wyrażeniami boost::bind. <code cpp> bind(f, bind(g, _1))(x); //f(g(x)) </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)). 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. <code cpp> typedef void (*fp)(int); std::vector<fp> v; std::for_each(v.begin(), v.end(), bind(_1, 5)); </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. <code cpp> typedef void (*fp)(int); std::vector<fp> v; std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5)); </code> Używając bind można komponować funkcje korzystając naraz z boost::bind i funkcji (algorytmów) z biblioteki standardowej. <code cpp> std::vector<int> ints; ints.push_back(7); ints.push_back(4); ints.push_back(12); ints.push_back(10); int count=std::count_if( ints.begin(), ints.end(), boost::bind( std::logical_and<bool>(), boost::bind(std::greater<int>(),_1,5), boost::bind(std::less_equal<int>(),_1,10))); std::cout << count << '\n'; std::vector<int>::iterator int_it = std::find_if( ints.begin(), ints.end(), boost::bind(std::logical_and<bool>(), boost::bind(std::greater<int>(),_1,5), boost::bind(std::less_equal<int>(),_1,10))); if (int_it != ints.end()) { std::cout << *int_it << '\n'; } </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. =====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ę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> double Foo(double x, double y) { return x + y; } boost::function<double(double)> f = boost::bind(Foo, _1, y); //tworzymy funktor przyjmujący jeden argument //i przypisujemy go do obiektu funkcyjnego </code> 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ą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 ==, !=, <, <=, >, >=. <code> !bind(f, ...) </code> Jest ekwiwalentem <code cpp> bind(logical_not(), bind(f, ...)) </code> gdzie logical_not jest funktorem przyjmującym jeden argument x i zwracający !x. <code cpp> bind(f, ...) op x </code> op jest operatorem relacji, wyrażniu temu odpowiada <code cpp> bind(relation(), bind(f, ...), x) </code> gdzie relation jest funktorem przyjmującym dwa argumenty a i b i zwracającym a op b. Przeciążenie tych operatorów umożliwia na konwencjonalne negowanie wyniku boost::bind: <code cpp> std::remove_if(first, last, !bind(&X::foo, _1)); //usun obiekt nie spełniający jakiegoś warunku </code> oraz na porównywanie wyników boost::bind z wartościami: <code cpp> std::find_if(first, last, bind(&X::name, _1) == "xyz"); </code> ze znakiem zastępczym: <code cpp> bind(&X::name, _1) == _2 </code> albo z innym wyrażeniem boost::bind: <code cpp> std::sort(first, last, bind(&X::name, _1) < bind(&X::name, _2)); </code> =====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. <code cpp> #include <iostream> #include <string> #include <boost/bind.hpp> class status { std::string name_; bool ok_; public: status(const std::string& name):name_(name),ok_(true) {} void break_it() { ok_=false; } bool is_broken() const { return ok_; } void report() const { std::cout << name_ << " is " << (ok_ ? "working nominally":"terribly broken") << '\n'; } }; int main() { std::vector<status*> statuses; statuses.push_back(new status("status 1")); statuses.push_back(new status("status 2")); statuses.push_back(new status("status 3")); statuses.push_back(new status("status 4")); statuses[1]->break_it(); statuses[2]->break_it(); std::for_each( statuses.begin(), statuses.end(), boost::bind(&status::report, _1)); return 0; } </code> Jeśli w pewnym momencie pisania projektu postanowimy używać wektora wskaźników do obiektów zamiast wektora obiektów lub sprytnych wskaźników w przypadku bind musimy zmienić tylko moment tworzenia obiektów, na przykład: <code cpp> std::vector<boost::shared_ptr<status> > statuses; statuses.push_back( boost::shared_ptr<status>(new status("status 1"))); statuses.push_back( boost::shared_ptr<status>(new status("status 2"))); statuses.push_back( boost::shared_ptr<status>(new status("status 3"))); statuses.push_back( boost::shared_ptr<status>(new status("status 4"))); </code> Użycie funkcji standardowych (std::mem_fun_ref i std::mem_fun) zmusza do zmian także pętli for_each po każdej modyfikacji typu przechowywanego przez wektor, a nawet uniemożliwia dalszą pracę (brak obsługi w przypadku użycia sprytnych wskaźników).
bind.1208155362.txt.gz
· ostatnio zmienione: 2008/04/14 08:42 przez
maciejp
Narzędzia strony
Pokaż stronę
Poprzednie wersje
Odnośniki
Do góry