Newsgroup: it.comp.lang.c++
Titolo: puntatori a funzione, anzi a metodo

Ciao a tutti.
Ho scritto un oggetto (obj1) con un campo che è un puntatore a funzione
inizializzato a NULL.
Un metodo di questo oggetto, se vede che tale puntatore è diverso da
NULL, fa eseguire la funzione puntata da questo puntatore.
Il metodo per "impostare" questo puntatore è SetFunction( ).
In un altro oggetto (obj2), ho provato a chiamare:
obj1 -> SetFunction( myFunc );
dove myFunc è un metodo (non statico) di obj2.
A questo punto g++ mi dice che il C++ non lo permette. 
ISO C++ forbids taking the address of an unqualified 
non-static member function to form a pointer to member function
Come mai non è possibile ? E cosa intende di preciso con 'unqualified' ?
E l'alternativa a questo approccio è solamente quella di creare un
oggetto che eredita da obj1 e ridefinisce la funzione che io volevo
rendere "variabile" ?
Grazie anticipatamente
Matic

 }
Grazie, tuttavia l'idea era quella di rendere B abbastanza "indipendente",
ovvero lui non sa che la funzione farà parte di un oggetto di tipo A.
Inoltre mi servirebbe di poter "memorizzare" l'idea che la funzione fa
parte dell'oggetto 'a' in un campo di B, visto che B::call() è chiamata
solo internamente (non è public).

 A questo punto g++ mi dice che il C++ non lo permette. 
#include <iostream>
struct A
{
     void callMe(){ std::cout << "called!" << std::endl; }
};
class B
{
     typedef void (A::*callee)();
     callee aCallee_;
  public:
     B( callee aCallee )
         : aCallee_(aCallee){}
     void call(A& callFor)
     {
         if(aCallee_ == 0 ) return;
         (callFor.*aCallee_)();
     }
};
int main()
{
     A a;
     B b(&A::callMe);
     b.call(a);
}
-- 
sbi

 ovvero lui non sa che la funzione farà parte di un oggetto di tipo A.
Premesso che, non conoscendo i dettagli, difficilmente si può capire se 
stai seguendo la strada migliore, un modo per permettere a una classe base 
di memorizzare un puntatore a una funzione membro della classe derivata è 
usare il cosiddetto Curiously Recurring Template Pattern (che taluni forse 
chiamano anche in altri modi).
 
  template< class DerivedT >
  class Base
  {
    protected:
    typedef void( DerivedT::* D_func_t)();
    D_func_t  func_;
 
    public:
    Base() : func_(0) { }
 
    void call_func()
    {
      if( func_ ) 
        (static_cast<DerivedT*>(this)->*func_)();
    }
  };
 
 
 
  class MyClass: public Base<MyClass>
  {
    public:
    void do_something() { }
 
    void set_do_something()
    {
      Base<MyClass>::func_ = &MyClass::do_something;  
    }
  };
 
Max



Grazie molto gentile.
Potresti spiegarmi una cosa a questo punto:
Io tenevo in un file.hpp la dichiarazione della classe e in file.cpp i
corpi dei metodi. Ex.
file.hpp:
class Pippo {
	void pluto ( );
	void paperino ( );
}
file.cpp:
void Pippo :: pluto ( ) { ... }
void Pippo :: paperino ( ) { ... }
Dopo aver aggiunto in file.hpp "template < class DerivedT >" per cercare
di seguire il tuo modello la mia carenza di conoscenza di sintassi c++ si
è fatta sentire. Al compilatore non sta piu bene che io scriva le
funzioni così: void Pippo :: pluto ( ) { ... }.
Ho provato a mettere così:
void Pippo<class DerivedT> :: pluto ( ) { ... }
ma ottengo errori a discrezione del compilatore su alcuni metodi e su
altri no; quando li ottengo sono questi:
Compiling epp.cpp
epp.cpp:443: error: specialization of void Epp<DerivedT>::bmwr_init() [with 
   DerivedT = DerivedT] after instantiation
epp.cpp:443: error: prototype for `void Epp<DerivedT>::bmwr_init() [with 
   DerivedT = DerivedT]' does not match any in class `Epp<DerivedT>'
epp.hpp:48: error: candidate is: void Epp<DerivedT>::bmwr_init() [with
   DerivedT = DerivedT]
epp.cpp:443: error: `void Epp<DerivedT>::bmwr_init() [with DerivedT =
DerivedT]
   ' and `void Epp<DerivedT>::bmwr_init() [with DerivedT = DerivedT]'
   cannot be overloaded
Su una ventina di metodi, 7-8 mi generano quest'errore, piu della meta
invece vanno bene.. non ho capito da cosa dipende. Forse dal fatto che
scrivere "void Pippo<class DerivedT> :: pluto ( )" non c'entra niente :P"
Quindi come devo mettere ?
Grazie ancora, è che questa sintassi mi è un po' oscura..
Ah un'altra domanda. il nome "DerivedT" è particolare ? No perché anche
dopo che l'avevo tolto completamente dai file il compilatore ancora me lo
ritirava fuori quando poteva con frasi tipo "..[with DerivedT = NomeMioT].."
Grazie
Ciao

 NomeMioT].."
No, 'DerivedT' è un nome di fantasia, che ho scelto perché evocativo della
categoria di tipi con cui puoi instanziare quel template. Se appare in un
errore, appare anche nel codice.
Se non hai mai usato classi o funzioni template, ti suggerirei di studiarti
bene l'argomento sul tuo manuale preferito.
Max

> chiamano anche in altri modi).
Provo a dire quello che sto' cercando di fare, così magari mi sapete dire
qual è la strada giusta da seguire.
Ho una classe che si occupa della comunicazione con una periferica esterna
via porta parallela (che chiamero Epp). Alcuni metodi di questa classe
consentono di interagire con questa periferica, e durante l'interazione
potrebbero dover venire printati messaggi informativi. Ho definito quindi
un metodo che viene richiamato da tutti i metodi che vogliono "printare"
un messaggio. L'idea che volevo seguire è quella di rendere indipendente
la classe Epp dal contesto in cui viene utilizzata, e permettere ad altre
classi che la usano di "ridirigere" questi messaggi man mano che venivano
generati.
Un esempio concreto: avendo scritto un front end grafico che utilizza
Epp mi piacerebbe che ogni messaggio generato da Epp finisca su una
textarea dell'oggetto grafico.
L'idea che avevo posto qui era quella di informare l'oggetto Epp che
volevo usare un altro metodo anziché il proprio per printare i messaggi
cercando di impostare un suo campo interno che doveva essere un puntatore
a funzione a una funzione esistente nell'oggetto grafico; in questo modo
Epp avendo fatto un controllo e resosi conto che questo puntatore era
diverso da NULL avrebbe richiamato la funzione puntata invece di scrivere
lui stesso su stdout.
Altre idee per implementare questo comportamento ?
Ps: l'oggetto grafico non eredita Epp, semplicemente ne conterrà 4 tra i
suoi campi (glie ne servono 4)
Grazie
Ciao
Mtq

 suoi campi (glie ne servono 4)
Se ho capito bene un'idea potrebbe essere:
class Logger
  {
  public:
    virtual void print(const std::string& s) = 0;
    virtual ~Logger() {}
  };
class Epp
  {
  public:
    Epp( Logger& log );
    /* eccetera */
  };
All'interno del costruttore di Epp occorre memorizzare un reference a Log
(si potrebbero usare anche degli smart pointer per gestire la lifetime del
Logger se e' il caso) e richiamare log.print() tutte le volte che occorre
"stampare" un messaggio.
La classe che utilizza Epp dovra' implementare un logger concreto che dentro
a print fa in modo di produrre l'output desiderato, su cout o su una form,
ecc...
Un esempio semplice che scrive su un file il log:
class FileLogger : public Logger
  {
  private:
    std::fstream& file;
  public:
    FileLogger( std::fstream& f ) : file(f) {}
    void print(const std::string& s ) { file << s; };
  };
class Myclass
  {
  private:
    std::fstream logfile;
    FileLogger logger;
    Epp epp;
  public: 
    MyClass(/*...*/) : logfile("Myclass.log"), logger(logfile), epp(logger)
     {}
  // Eccetera...
  };
Occhio che nell'esempio potrei averci seminato degli errori :-)
-- 
Qual'e' il colmo per uno studente-lavoratore?
Avere un quadrato che si crede un rettangolo.

 
Grazie mille era proprio questo che cercavo.
Ciao
Maeatz

Matic wrote:

Di niente!
I metodi virtuali e l'ereditarieta' sono nati proprio per superare le
limitazioni di puntatori a funzione e simili...
...beh, forse non solo per quello :-)
