Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Funktio-osoittimet

Mazzimo [16.04.2007 19:07:47]

#

Funktio-osoittimet toimivat, kuten niiden nimikin jo kertoo: ne säilövät funktioiden osoitteita. C++:ssa jokaisella funktiolla on oma osoitin ja näitä osoittimia voidaan ottaa talteen ja niitä kutsumalla myös kutsua osoittimen taakse kätkeytyviä funktioita. Tämä on hyvin tehokas tapa tehdä ns. callback, eli isäntä asettaa luodulle instanssille callback-funktion, jota luotu instanssi sitten tarpeen vaatiessa kutsuu.

Funktio-osoittimet ovat C++:ssa hyvin vaikeita itsessään, mutta niiden käsittelyyn on myös mahdollista tehdä luokka. Tämän luokan taakse voidaan kätkeä niin yksittäiset funktiot, kuin luokan instanssien jäsenfunktiot. Lisäksi funktion kutsuminen on tämän luokan avulla helppoa.

Tekemäni luokka sisältää seuraavat ominaisuudet:

* yksittäisen funktio tallettaminen
* instanssin jäsenfunktion tallettaminen
* oma parametri (vain yksi)
* oma paluuarvo (void ei tuettuna)
* () -operaattori tallennetun funktion suorittamiseen
* funktio-osoittimen kopioimisen toiseen
* assertion null-osoittimia varten
* null-osoittimen tarkistus

Ensimmäisessä koodilistauksessa on headeri, joka sisältää itse funktio-osoittiminen toteutuksen. Loput kolme koodia ovat esimerkkejä (tökeröitä sellaisia :P).

FunctionPtr.h

ifndef __function_ptr_h__
#define __function_ptr_h__


/******************* (c) Matti Lankinen ********************
* Tämä tiedosto sisältää luokan funktioiden (yksittäisten
* ja jäsenfunktioiden osoittimien tallennukseen ja käyttää
* niitä näin callback:eina.
*
* Koodi on vapaasti käytettävissä ja muunneltavissa.
*/


#include <cassert>



/** FunktioPtr-luokan tarvitsema jäsen joka sisältää virtuaalimetodit funktio-osoittimien käsittelyyn.
@remarks
   Tämä on abstrakti kantaluokka josta peritään luokat _SingleBuilder
   ja _MemberBuilder.
*/
template <typename ret, typename args>
class _BuilderBase
{
   public:

      _BuilderBase() {}
      virtual ~_BuilderBase() {}
      virtual ret operator() (args a) const = 0;
      virtual _BuilderBase* copy() const = 0;

};




/** FunktioPtr-luokan tarvitsema jäsen joka sisältää virtuaalimetodit funktio-osoittimien käsittelyyn.
@remarks
   Tämä on abstrakti kantaluokka josta peritään luokat _SingleBuilder
   ja _MemberBuilder.
@remarks
   Erikoistapaus void-tyyppiselle parametrille.
*/
template <typename ret>
class _BuilderBase <ret, void>
{
   public:

      _BuilderBase() {}
      virtual ~_BuilderBase() {}
      virtual ret operator() () const = 0;
      virtual _BuilderBase* copy() const = 0;

};




/** FunktioPtr-luokan tarvitsema kapselointi-instanssi funktion osoittimelle.
@remarks
   Periytetty _BuilderBase-luokasta.
*/
template <typename ret, typename args>
class _SingleBuilder : public _BuilderBase<ret, args>
{
   public:

      typedef ret (*Pointer)(args);

      _SingleBuilder(Pointer ptr)
         : myPtr(ptr)
      {
      }


      virtual ret operator() (args a) const
      {
         return myPtr(a);
      }


      virtual _BuilderBase<ret,args>* copy() const
      {
         return new _SingleBuilder<ret,args>( myPtr );
      }



   protected:

      Pointer   myPtr;

};




/** FunktioPtr-luokan tarvitsema kapselointi-instanssi funktion osoittimelle.
@remarks
   Periytetty _BuilderBase-luokasta.
@remarks
   Erikoistapaus void-tyyppiselle parametrille.
*/
template <typename ret>
class _SingleBuilder<ret, void> : public _BuilderBase<ret, void>
{
   public:

      typedef ret (*Pointer)(void);

      _SingleBuilder(Pointer ptr)
         : myPtr(ptr)
      {
      }


      virtual ret operator() () const
      {
         return myPtr();
      }


      virtual _BuilderBase<ret,void>* copy() const
      {
         return new _SingleBuilder<ret,void>( myPtr );
      }



   protected:

      Pointer   myPtr;

};

/** FunktioPtr-luokan tarvitsema kapselointi-instanssi luokan jäsenfunktion osoittimelle.
@remarks
   Periytetty _BuilderBase-luokasta.
*/
template <typename ret, typename args, typename T>
class _MemberBuilder : public _BuilderBase<ret, args>
{
   public:

      typedef ret (T::* Pointer)(args);

      _MemberBuilder(Pointer ptr, T* instance)
      {
         myInstance = instance;
         myPtr = ptr;
      }


      virtual ret operator() (args a) const
      {
         return (myInstance->*myPtr)(a);
      }


      virtual _BuilderBase<ret,args>* copy() const
      {
         return new _MemberBuilder<ret,args,T>(myPtr, myInstance);
      }


   protected:

      T*         myInstance;
      Pointer      myPtr;


};



/** FunktioPtr-luokan tarvitsema kapselointi-instanssi luokan jäsenfunktion osoittimelle.
@remarks
   Periytetty _BuilderBase-luokasta.
@remarks
   Erikoistapaus void-tyyppiselle parametrille.
*/
template <typename ret, typename T>
class _MemberBuilder<ret,void,T> : public _BuilderBase<ret, void>
{
   public:

      typedef ret (T::* Pointer)(void);

      _MemberBuilder(Pointer ptr, T* instance)
      {
         myInstance = instance;
         myPtr = ptr;
      }


      virtual ret operator() () const
      {
         return (myInstance->*myPtr)();
      }


      virtual _BuilderBase<ret,void>* copy() const
      {
         return new _MemberBuilder<ret,void,T>(myPtr, myInstance);
      }


   protected:

      T*         myInstance;
      Pointer      myPtr;


};

/** tämä luokka sisältää halutun tyyppisen funktion pointterin.
@remarks
   pointterin voi luoda funktiosta, tai tietyn luokan instanssin
   jäsenfunktiosta. Luokka on malliluokka, joten siitä vuo luoda
   minkä tyyppisiä funktio-osoittimia tahansa. Funktiolla on paluu-
   arvo sekä YKSI (1) parametri, jotka käyttäjä saa itse valita
   instanssia luodessaan. Jäsenfunktion voi ottaa minkä tahansa
   luokan instanssista!
*/
template <typename ret, typename args> class FunctionPtr
{
   public:

      typedef ret (*Pointer)(args);

/** Luo tyhjän instanssin */
      FunctionPtr()
         : ptrCapsule(0)
      {
      }



      /** Luo alustetun instanssin olemassa olevasta osoittimesta.
      @param ptr Osoitin funktioon.
      */
      FunctionPtr( Pointer ptr )
      {
         ptrCapsule = new _SingleBuilder<ret,args>(ptr);
      }



      /** Luo alustetun instanssin halutun luokan olion jäsenfunktioista.
      @param (ret (T::*f)(args)) osoitin luokan jäsenfunktioon
      @param instance Osoitin olioista, jonka funktio toteutetaan.
      */
      template <class T> FunctionPtr(ret (T::*f)(args), T* instance)
      {
         ptrCapsule = new _MemberBuilder<ret,args,T>(f, instance);
      }



      /** Luo uuden instanssin käyttäen käyttäen kopioitavan luokan _BuilderBase-instanssia.
      @remarks
         Tämä ei ole tavalliselle käyttäjälle sillä kyseiset instanssit ovat
         vain FunctionPtr-luokan saavutettavissa. Tätä käytetäänkin vain
         kopiomuodostimessa FunctionPtr-luokan instansseille, joita käyttäjä
         voi käyttää.
      @param capsule Kapseli, joka sisältää toiminnallisuuden.
      */
      FunctionPtr( _BuilderBase<ret,args>* capsule )
      {
         ptrCapsule = capsule;
      }



      /** Luo uuden instanssin kopioiden sen annetusta instanssista.
      @remarks
         Luodun instanssin ()-operaattori tekee siis täysin samat
         asiat, mitä kopioitavan instanssin operaattori.
      @param other Instanssi, josta tiedot kopioidaan.
      */
      FunctionPtr(const FunctionPtr& other)
         : ptrCapsule(0)
      {
         if( other.getPtrCapsule() )   ptrCapsule = other.getPtrCapsule()->copy();
      }



      /** Tuhoaa instanssin ja vapauttaa mahdollisesti osoitinkapselin varaamaan vapaan muistin. */
      ~FunctionPtr()
      {
         release();
      }



      /** Suorittaa instanssissa olevan funktion.
      @remarks
         Sinun täytyy olla varma, että instanssissa on varmasti funktio-osoitin
         sillä tämä ei tarkasta sitä ja seuraukset huolimattomasta käytöstä
         voivat olla vakavat.
      @param a Halutun tyyppinen instanssi, jonka funktio ottaa parametrikseen.
      @returns Halutun tyyppisen instanssin, jonka funktio palauttaa.
      */
      ret operator() (args a) const
      {
         assert( ptrCapsule );
         return (*ptrCapsule)(a);
      }



      /** Palauttaa käyttäjälle instanssissa olevan kapselin osoitteen.
      @remarks
         Palautettava arvo on kantaluokka, mutta instanssissa saattaa olla
         jompi kumpi tämän luokan periytyvistä luokista riippuen instanssin
         luontitavasta.
      @returns Kapseli, joka sisältää osoittimen.
      */
      _BuilderBase<ret,args>* getPtrCapsule() const
      {
         return ptrCapsule;
      }



      /** Tuhoaa instanssissa olevan funktiokapselin muistista. */
      void release()
      {
         if( ptrCapsule )   delete ptrCapsule;
         ptrCapsule = 0;
      }

/** Kopioi intanssille tiedot toisesta instanssista.
      @param other Instanssi, josta tiedot kopioidaan.
      @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin.
      */
      FunctionPtr& operator= (const FunctionPtr& other)
      {
         release();
         ptrCapsule = other.getPtrCapsule()->copy();
         return *this;
      }



      /** Kopioi intanssille tiedot kapselista.
      @param other Kapseli, josta tiedot kopioidaan.
      @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin.
      */
      FunctionPtr& operator= (_BuilderBase<ret,args>* capsule)
      {
         release();
         ptrCapsule = capsule;
         return *this;
      }



      /** Kertoo, onko instanssiin asetettu funktio.
      @returns Palauttaa true, jos instanssi on tyhjä, false jos ei ole.
      */
      bool isNull() const
      {
         return !ptrCapsule;
      }

private:

      _BuilderBase<ret,args>*   ptrCapsule;      // funktio-osoittimen sisältävä luokka

};

/** tämä luokka sisältää halutun tyyppisen funktion pointterin.
@remarks
   pointterin voi luoda funktiosta, tai tietyn luokan instanssin
   jäsenfunktiosta. Luokka on malliluokka, joten siitä vuo luoda
   minkä tyyppisiä funktio-osoittimia tahansa. Funktiolla on paluu-
   arvo sekä YKSI (1) parametri, jotka käyttäjä saa itse valita
   instanssia luodessaan. Jäsenfunktion voi ottaa minkä tahansa
   luokan instanssista!
@remarks
   Erikoistapaus void-tyyppiselle parametrille.
*/
template <typename ret> class FunctionPtr<ret, void>
{
   public:

      typedef ret (*Pointer)(void);

/** Luo tyhjän instanssin */
      FunctionPtr()
         : ptrCapsule(0)
      {
      }



      /** Luo alustetun instanssin olemassa olevasta osoittimesta.
      @param ptr Osoitin funktioon.
      */
      FunctionPtr( Pointer ptr )
      {
         ptrCapsule = new _SingleBuilder<ret,void>(ptr);
      }



      /** Luo alustetun instanssin halutun luokan olion jäsenfunktioista.
      @param (ret (T::*f)(args)) osoitin luokan jäsenfunktioon
      @param instance Osoitin olioista, jonka funktio toteutetaan.
      */
      template <class T> FunctionPtr(ret (T::*f)(void), T* instance)
      {
         ptrCapsule = new _MemberBuilder<ret,void,T>(f, instance);
      }



      /** Luo uuden instanssin käyttäen käyttäen kopioitavan luokan _BuilderBase-instanssia.
      @remarks
         Tämä ei ole tavalliselle käyttäjälle sillä kyseiset instanssit ovat
         vain FunctionPtr-luokan saavutettavissa. Tätä käytetäänkin vain
         kopiomuodostimessa FunctionPtr-luokan instansseille, joita käyttäjä
         voi käyttää.
      @param capsule Kapseli, joka sisältää toiminnallisuuden.
      */
      FunctionPtr( _BuilderBase<ret,void>* capsule )
      {
         ptrCapsule = capsule;
      }



      /** Luo uuden instanssin kopioiden sen annetusta instanssista.
      @remarks
         Luodun instanssin ()-operaattori tekee siis täysin samat
         asiat, mitä kopioitavan instanssin operaattori.
      @param other Instanssi, josta tiedot kopioidaan.
      */
      FunctionPtr(const FunctionPtr& other)
         : ptrCapsule(0)
      {
         if( other.getPtrCapsule() )   ptrCapsule = other.getPtrCapsule()->copy();
      }



      /** Tuhoaa instanssin ja vapauttaa mahdollisesti osoitinkapselin varaamaan vapaan muistin. */
      ~FunctionPtr()
      {
         release();
      }



      /** Suorittaa instanssissa olevan funktion.
      @remarks
         Sinun täytyy olla varma, että instanssissa on varmasti funktio-osoitin
         sillä tämä ei tarkasta sitä ja seuraukset huolimattomasta käytöstä
         voivat olla vakavat.
      @param a Halutun tyyppinen instanssi, jonka funktio ottaa parametrikseen.
      @returns Halutun tyyppisen instanssin, jonka funktio palauttaa.
      */
      ret operator() () const
      {
         assert( ptrCapsule );
         return (*ptrCapsule)();
      }



      /** Palauttaa käyttäjälle instanssissa olevan kapselin osoitteen.
      @remarks
         Palautettava arvo on kantaluokka, mutta instanssissa saattaa olla
         jompi kumpi tämän luokan periytyvistä luokista riippuen instanssin
         luontitavasta.
      @returns Kapseli, joka sisältää osoittimen.
      */
      _BuilderBase<ret,void>* getPtrCapsule() const
      {
         return ptrCapsule;
      }



      /** Tuhoaa instanssissa olevan funktiokapselin muistista. */
      void release()
      {
         if( ptrCapsule )   delete ptrCapsule;
         ptrCapsule = 0;
      }

/** Kopioi intanssille tiedot toisesta instanssista.
      @param other Instanssi, josta tiedot kopioidaan.
      @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin.
      */
      FunctionPtr& operator= (const FunctionPtr& other)
      {
         release();
         ptrCapsule = other.getPtrCapsule()->copy();
         return *this;
      }



      /** Kopioi intanssille tiedot kapselista.
      @param other Kapseli, josta tiedot kopioidaan.
      @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin.
      */
      FunctionPtr& operator= (_BuilderBase<ret,void>* capsule)
      {
         release();
         ptrCapsule = capsule;
         return *this;
      }



      /** Kertoo, onko instanssiin asetettu funktio.
      @returns Palauttaa true, jos instanssi on tyhjä, false jos ei ole.
      */
      bool isNull() const
      {
         return !ptrCapsule;
      }



   private:

      _BuilderBase<ret,void>*   ptrCapsule;      // funktio-osoittimen sisältävä luokka

};


#endif

Esimerkki 1 - helppo

#include <string>
#include <iostream>
#include "FunctionPtr.h"



bool writeMessageA(const std::string& m)
{
   std::cout << "Hello " << m << "!\n";
   return true;
}


bool writeMessageB(const std::string& m)
{
   std::cout << "Bye bye " << m << "!\n";
   return true;
}



typedef FunctionPtr<bool, const std::string&> Pointer;

int main()
{
   std::string name;
   std::cin >> name;

   Pointer ptr(writeMessageA);
   ptr( name );

   ptr = Pointer(writeMessageB);
   ptr( name );

   return 0;
}

Esimerkki 2 - keskitaso

#include <string>
#include <iostream>
#include "FunctionPtr.h"



bool writeMessageA(const std::string& m)
{
   std::cout << "Hello " << m << "!\n";
   return true;
}


bool writeMessageB(const std::string& m)
{
   std::cout << "Bye bye " << m << "!\n";
   return true;
}



typedef FunctionPtr<bool, const std::string&> Pointer;

int main()
{
   std::string name;
   std::cin >> name;

   // luodaan osoitintaulukko ;)
   Pointer ptrArray[2];

   ptrArray[0] = Pointer(writeMessageA);
   ptrArray[1] = Pointer(writeMessageB);


   // annetaan käyttäjän valita viesti
   int select;
   std::cout << "Select message (0-1): ";
   std::cin >> select;


   if( select >= 0 && select < 2 ) ptrArray[select](name);

   return 0;
}

Esimerkki 3 - vaikea

#include <string>
#include <iostream>
#include <map>
#include "FunctionPtr.h"



class Example
{
	public:

		Example(const std::string& name) : mName(name) {}


		bool messageHello(const std::string& name)
		{
			std::cout << mName << " says 'hello!' to you " << name << ".\n";
			return true;
		}

		bool messageBye(const std::string& name)
		{
			std::cout << mName << " says 'bye bye!' to you " << name << ".\n";
			return true;
		}


	private:

		std::string mName;

};



typedef FunctionPtr<bool, const std::string&> Pointer;
typedef std::map<char, Pointer> PtrMap;

int main()
{
   std::string name;
   std::cin >> name;



   // luodaan yksi instanssi, jonka jäsenfunktioihin osoitetaan
   Example someone("Peter");

   // luodaan kartta ja lisätään siihen muutama avain
   PtrMap keys;
   keys['1'] = Pointer(&Example::messageHello, &someone);
   keys['2'] = Pointer(&Example::messageBye, &someone);

   // annetaan käyttäjän valita viesti
   char select;
   std::cout << "Select message (1-2): ";
   std::cin >> select;

   // tulostetaan se - tarkistus on hyvin tärkeää, sillä jos avaimeksi annettaisiin
   // esimerkiksi '3', olisi osoitin NULL ja siitä ei hyvä seuraisi...
   // jos otat tarkistuksen pois, laukaistaan assertio null-osoittimen sattuessa
   if( !keys[select].isNull() ) keys[select]( name );

   return 0;
}

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta