JLI Spieleprogrammierung Foren-Übersicht JLI Spieleprogrammierung

 
 FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen 
 medals.php?sid=f08149635799fd704869c634de33ecf6Medaillen   RegistrierenRegistrieren   ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin 

[boost]::bind

 
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> Tutorials
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
David
Super JLI'ler


Alter: 38
Anmeldedatum: 13.10.2005
Beiträge: 315

Medaillen: Keine

BeitragVerfasst am: 06.01.2007, 16:28    Titel: [boost]::bind Antworten mit Zitat

boost::bind

Einleitung

In diesem Tutorial geht es um die Funktionalitaet boost::bind von boost. Da diese in erster Linie eine Erweiterung von den Standardfunktionen std::bind1st und std::bind2nd ist, handelt der erste Abschnitt von besagten Funktionen.
Die weiteren Abschnitte beschreiben die Verwendung von boost::bind mit Funktionen, Funktionszeigern, Funktionsobjekten und Klassenmethoden, außerdem wird auf die ueberladenen Operatoren von boost::bind eingegangen.


Binden von Parametern an Funktionen

Manche werden sich vielleicht fragen, was boost::bind oder std:bind1st und std::bind2nd ueberhaupt machen. Grundsaetzlich sind dies Werkzeuge die es ermoeglichen Parameter einer Funktion fest zu binden. Heraus kommt dann ein Funktionsobjekt das weniger Parameter benoetigt als das Urspruengliche.
Ein kleines Beispiel (Pseudocode):

CPP:
#include <iostream>

int Add( int a, int b )
{
   return a + b;
}

int main()
{
   BindFun = Bind( Add, 10 );
   int result = BindFun( 20 );
   std::cout << "Result: " << result << std::endl;
   std::cin.get();
}


Der Aufruf von BindFun( 20 ) entspricht hier dem Aufruf von Add( 10, 20 ) wobei der erste Parameter durch Bind( Add, 10 ) fest auf den Wert 10 gesetzt wird. Dieses Vorgehen nennt man Parameter binden.
Die STL bietet hierfuer zwei Funktionen, std::bind1st und std::bind2nd, welche leider schnell an ihre Grenzen stoßen und somit wenig Flexibilitaet bereitstellen. Es koennen z.B. immer nur die ersten beiden Parameter gebunden werden. Beim Verwendung von Klassenmethoden ist sogar nur ein Parameter moeglich.
Im folgenden werden drei Beispiele gezeigt, welche die einzelnen Moeglichkeiten demonstrieren sollen.

CPP:
#include <iostream>
#include <functional>

int Add( int a, int b )
{
   return a + b;
}

int main()
{
   std::cout << std::bind1st( std::ptr_fun( Add ), 10 )( 20 );
   std::cin.get();
}


Das Kernstueck ist hierbei die erste Zeile in der main Funktion. Hier wird der erste Parameter der Funktion Add an den Wert 10 gebunden und gleich darauf der Resultierende Funktor mit dem Wert 20 aufgerufen.
aequivalent funktioniert das auch mit dem zweiten Parameter, statt dem ersten.

CPP:
std::bind2nd( std::ptr_fun( Add ), 10 )( 20 )


Im ersten Fall entspricht der Aufruf des Funktors dem eines Add( 10, 20 ) im zweiten Fall dem eines Add( 20, 10 ). Das Ergebnis ist hierbei natuerlich identisch, die Art des Aufrufs aber keineswegs.
Hier ist die Funktionsvielfalt aber bereits erschoepft, mehr als die ersten beiden Parameter lassen sich nicht manipulieren. Allerdings werden nicht nur Funktionen und Funktionszeiger unterstuetzt, sondern auch Funktionsobjekte und Klassenmethoden. Hierzu zwei kleine Beispiele.
CPP:
#include <iostream>
#include <functional>

struct Foobar
   : public std::binary_function< int, int, int >
{
   int operator()( int a, int b ) const
   {
      return a + b;
   }
};

int main()
{
   std::cout << std::bind2nd( Foobar(), 10 )( 20 );
   std::cin.get();
}


Das Ergebnis ist hier auch das selbe, es wird allerdings ein Funktionsobjekt verwenden, statt einer normalen Funktion.
Wie hier schoen zu sehen ist, muss die Klasse von std::binary_function oder std::unary_function erben.
Das binden von Klassenmethoden verhaelt sich aber ein wenig anders. Hierzu wird die Funktion std::mem_fun zur Hilfe genommen, naemlich um einen Funktionsobjekt Adapter zur entsprechenden Memberfunktion zu erzeugen.
Leider hat diese Funktion starke Einschraenkungen, sie erlaubt naemlich maximal einen Parameter bei Memberfunktionen. Daher wird weiter Unten kurz die bessere boost Loesung vorgestellt: boost::mem_fn.
CPP:
#include <iostream>
#include <string>
#include <functional>

struct Foobar
{
   void print( std::string text) const
   {
      std::cout << text;
   }
};

int main()
{
   Foobar foo;
   std::string text( "Hallo Welt" );
   std::bind1st( std::mem_fun( &Foobar::print ), &foo )( text );
   std::cin.get();
}


Dieses Beispiel zeigt das Binden von Memberfunktionen. Allerdings wird diesmal kein Parameter gebunden sondern lediglich eine vorhandene Instanz mit einer Memberfunktion. Es wird also ein Funktor erzeugt der auf eine Memberfunktion verweist.
Wie vorhin bereits angesprochen bietet boost eine bessere Alternative zu std::mem_fun. Dieser ist es moeglich mehr als maximal einen Parameter, bei einer Memberfunktion, zu akzeptieren, was einen ganz enormen Vorteil bringt.

Binden von Funktionen
Nun aber endlich zum tatsaechlichen Thema dieses Threads. Alle oben vorgestellten Loesungen sind mit boost::bind loesbar. Außerdem bietet boost::bind noch einige zusaetzliche Features die es zu einem ziemlich maechtigen Tool in Sache Funktionsbinden macht.
Im folgendes erstmal ein kleiner Codeschnippsel:
CPP:
#include <iostream>
#include <string>
#include <boost/bind.hpp>

int Add( int a, int b )
{
   return a + b;
}

int main()
{
   using namespace boost;

   std::cout << bind( Add, 10, _1 )( cref( 20 ) ) << std::endl;
   std::cin.get();
}


Boost wandelt die uebergebene Funktion automatisch in einen Funktionszeiger um, der zusaetzliche Aufruf eines std::ptr_fun faellt hier also weg. Außerdem bietet boost::bind die Moeglichkeit jeden Parameter, oder auch mehrere, fest zu binden. Alle jene Parameter die nicht gebunden werden sollen werden Platzhalter uebergeben, im Beispiel durch die _1 sichtbar.
Diese Platzhalter geben den Index der uebergebenen Argumente an.

CPP:
boost::bind( f, _1, _1, _1 )( x ) => f( x, x, x )
boost::bind( f, _1, _2, _3 )( x, y, z ) => f( x, y, z )
boost::bind( f, _1, _1, _2 )( x, y, z ) => f( x, x, y )
boost::bind( f, _2, _1, _3 )( x, y, z ) => f( y, x, z )
boost::bind( f, _2, 10, _2 )( x, y ) => f( y, 10, y )


Es ist leicht ersichtlich was fuer eine Vielfalt an Kombinationsmoeglichkeiten hier geboten wird.
Es ist aber noch mehr moeglich, z.B. koennen Objekte "by reference" gebunden werden, folgender Codeschnippsel soll dies demonstrieren.
CPP:
#include <iostream>
#include <string>
#include <boost/bind.hpp>

void Add( int a, int b, int &result )
{
   result = a + b;
}

int main()
{
   using namespace boost;

   int a = 10;
   int b = 20;
   int c;

   bind( Add, _1, a, ref( c ) )( b );
   std::cout << c;
   std::cin.get();
}

Die Funktionen boost::ref und boost::cref erlauben das Binden von konstanten- und nicht konstanten Referenzen.

Binden von Funktionsobjekten
In diesem Teil wird das Binden von Parametern unter Verwendung von Funktionsobjekten vorgestellt. Funktionsobjekte wurden schon weiter oben gezeigt, dies sind einfach Objekte deren Klasse ueber einen Funktor-operator verfuegt.
Boost::bind benoetigt allerdings, im Gegensatz zur STL Variante, keine Basisklasse fuer die Funktionsobjekt Klasse. Im Grund wird jede Klasse, ohne Einschraenkungen, klaglos genommen.
CPP:
#include <iostream>
#include <string>
#include <boost/bind.hpp>

struct Foobar
{
   int operator()( int a, int b ) const
   {
      return a + b;
   }
};


int main()
{
   using namespace boost;

   std::cout << bind< int >( Foobar(), _1, 10 )( cref( 20 ) );
   std::cin.get();
}

Was hier speziell zu beachten ist, ist der Templateparameter hinter bind. Dieser gibt den Rueckgabetyp des Operators an.

Tipp: Da manche Compiler allerdings Probleme mit der o.g. Syntax haben, wird eine alternative Schreibweise angeboten. In diesem Fall saehe das so aus:
CPP:
bind( type< int >(), Foobar(), _1, 10 )( cref( 20 ) )


Binden von Klassenmethoden
Als letztes soll das Binden von Parametern unter Verwendung von Klassenmethoden (Memberfunktionen) vorgestellt werden.
Boost::bind bietet nun auch endlich die Moeglichkeit dies wie gewuenscht durchzufuehren (ohne Umwege). Als kleines Hilfsmittel wird hier die verallgemeinerte Ausfuehrung von std::mem_fun, naemlich boost::mem_fn, verwendet.
CPP:
#include <iostream>
#include <boost/bind.hpp>

struct Foobar
{
   int Add( int a, int b )
   {
      return a + b;
   }
};


int main()
{
   using namespace boost;

   Foobar bar;

std::cout << bind( mem_fn( Foobar::Add ), &bar, _1, 10 )( cref( 20 ) );
}


Hier muss zwar auch eine Instanz uebergeben werden, was nicht verwunderlich ist da Memberfunktionen ja nicht einzigartig sind sondern sich auf ein jeweiliges Objekt beziehen, allerdings erlaubt es boost::bind Parameter ohne große Probleme festzubinden, wobei die Anzahl der Parameter nicht nur auf zwei begrenz ist (siehe STL Loesung).
Einen Blick wert ist aber die uebergabe der Klasseninstanz. Dies kann auf verschiedene Arten geschehen. Zum einen die, welche im letzten Beispiel verwendet wurde, per Zeiger auf das Objekt. Es ist aber auch moeglich eine Referenz oder eine Kopie des Objekts zu verwenden.
CPP:
bind( &X::fun, ref( obj ), _1, _1 )( x ) => obj.fun( x, x )
bind( &X::fun, &obj, _1, _1 )( x ) => ( &obj )->fun( x, x )
bind( &X::fun, obj, _1, _1 )( x ) => interne Kopie.fun( x, x )


Es werden ebenfalls Smartpointer akzeptiert, von diesen wird dann eine interne Kopie angelegt. Ein kleines Beispiel soll dies demonstrieren.
CPP:
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>

struct Foobar
{
   int Add( int a, int b )
   {
      return a + b;
   }
};


int main()
{
   using namespace boost;

   shared_ptr< Foobar > ptr( new Foobar );

std::cout << bind( mem_fn( Foobar::Add ), ptr, _1, 10 )( cref( 20 ) );
}

Zu beachtest ist, das bei den zwei letzten Methoden immer eine interne Kopie erstellt wird. D.h. das original Objekt wird nicht mehr direkt referenziert. Bei boost::shared_ptr kann dies durchaus kein Problem sein, aber vor allem bei Kopien des Originals hat der Aufruf einer Methode evtl nicht die gewuenschte Auswirkung.

Praktische Verwendung
In der Praxis findet diese Technik viele Anwendungsmoeglichkeiten. Hat man z.B. ein GUI System in welchem jedes Element durch eine ID gekennzeichnet ist, so koennte man festlegen das bei jedem Eventaufruf (ueber einen Funktor z.B.) zuerst die entsprechende ID uebergeben werden soll.
Um Fehlerquellen zu vermeiden und sich Tipparbeit zu sparen koennte man diesen Parameter fuer das jeweilige Objekt festbinden.

Schlusswort

Dies ist nun schon das Ende der kleine Einfuehrung in einen kleinen Teil von boost. Wer Fragen zum Thema hat kann gern an reinigdavid@hotmail.com eine E-Mail senden.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> Tutorials Alle Zeiten sind GMT
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.


Powered by phpBB © 2001, 2005 phpBB Group
Deutsche Übersetzung von phpBB.de

Impressum