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. 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 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 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(&nine_arguments,_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 } bind(f, _2, _3, _1, _1, 0, 0, _3, 0, _2)(x, y, z); //utworzenie tymczasowego funktora i wywołanie </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. Jak wynika z przykładu, można dowolnie "mieszać" argumenty ze stałymi jak i ich kolejność. =====Użycie z funktorami===== 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). <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 bind akceptuje te wskażniki jako pierwszy argument. 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 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ę 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); </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 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 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 bind może być bardzo dobrze zastosowana do kompozycji funkcji. Trzeba zaznaczyć, że pierwszy argument 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 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 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 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 =. <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 bind pojawiły się w Boost 1.33 Obiekty funkcyjne produkowane przez 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. What this means in practice is that you can conveniently negate the result of bind: Przeciążenie tych operatorów umożliwia na konwencjonalne negowanie wyniku bind: <code cpp> std::remove_if(first, last, !bind(&X::foo, _1)); //usun obiekt nie spełniający jakiegoś warunku </code> and compare the result of bind against a value: oraz na porównywanie wyników 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 bind: <code cpp> std::sort(first, last, bind(&X::name, _1) < bind(&X::name, _2)); </code> =====Przykład użycia===== 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)); } </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.1208020647.txt.gz
· ostatnio zmienione: 2008/04/12 19:17 przez
maciejp
Narzędzia strony
Pokaż stronę
Poprzednie wersje
Odnośniki
Do góry