Newsgroup: it.comp.lang.c++
Titolo: Macchina a stati

Salve a tutti,
vorrei realizzare una macchina a stati a cui ad ogni stato e' associato
una funzione; in questo modo percorrendo i diversi stati vengono
richiamate le varie funzioni associate. Le funzioni possono anche essere
membri di una classe.
Avete qualche idea su come si potrebbe fare?
Ho provato a definirmi un puntatore ad un membro di una classe ma il
compilatore mi da' errore, mentre se definisco un puntatore ad una
funzione globale non mi da' nessun errore; qualcuno sa spiegarmi come
mai?
Riporto il codice che mi da' l'errore:
void funz1() {....};
class Classe
{
    void (*p1) ();
    void funzClasse() {...}
public:
    Classe()
    {
        p1 = funzClasse; // ERRORE
        p1 = funz1; // TUTTO OK come mai?
    }
}
Grazie a tutti.
Saluti Antonio.

Antonio,
p1 vuole una funzione senza nessun parametro, mentre funzClasse(),
essendo
metodo di una classe, ha il primo parametro invisibile 'this' e quindi
l'assegnamento non e' accettato.
HTH
Claudio


mai?
Credo che non capiro` mai da dove nasca la confusione fra
funzioni-membro-non-statiche, e "funzioni" vere; le prime, che
sarebbe bello chiamare "metodi" (termine abbastanza diffuso),
hanno una natura talmente diversa dalle seconde.  Comunque,
e` una confusione assai diffusa.  Da un punto di vista "astratto",
una funzione-di-quelle-vere *non ha STATO*: non vi e`
nessuna "informazione ambientale" implicata dalla funzione;
e`, appunto, una FUNZIONE.  Un *metodo*, visto che e`
sempre associato a un particolare oggetto (non puo` essere
chiamato "in assoluto", ma solo SU di un particolare oggetto),
ha invece potenzialmente tutto lo STATO che l'oggetto puo`
portarsi dietro -- e inoltre, la IDENTITA` dell'oggetto stesso
(questione distinta da quella dello STATO).
Da un punto di vista "concreto", possiamo dire che un metodo
ha un "primo argomento speciale, implicito, e nascosto" -- quel
puntatore-a-oggetto che entro il metodo stesso si chiama this.
Una funzione-che-non-e`-un-metodo (funzione libera, funzione
membro statico di una classe) non ha nulla del genere; quindi
chiaramente non sono cose "intercambiabili".
Venendo al tuo problema.  Supponiamo che la classe base
comune di tutti gli oggetti sui quali puoi voler invocare dei
metodi si chiami Base; dovra` avere come virtuali (tipicamente
virtuali puri, ma cio` non e` strettamente necessario) tutti i
metodi che puoi voler invocare.  Supponiamo che tutti questi
metodi prendano un double e tornino un double.  Allora, la
struttura "puntatore a metodo e relativo oggetto" sara` simile a:
    class Pamero {
        Base* pOggetto;
        typedef double (Base::* pMetodo_t)(double);
        pMetodo_t pMetodo;
    public:
        Pamero(Base *pO, pMetodo_t pM):
            pOggetto(pO), pMetodo(pM)
        {}
        double operator()(double x) {
            return (pOggetto->*pMetodo)(double);
        }
    };
Questa e` scritta in modo che un Pamero sia un funtore,
cioe` usabile esattamente come useresti un puntatore a
funzione.  Notiamo inoltre che non abbiamo definito il
costruttore di copia, l'assegnamento di copia, e il distruttore,
perche` quelli che automaticamente fornisce il compilatore
vanno qui benissimo.  Pamero non ha invece qui un
costruttore di default, il che a volte da' problemi perche`
vuol dire che non si puo` definire un Pamero senza,
contestualmente, inizializzarlo; potremmo decidere che
il ctor di default faccia un "Pamero nullo", cioe` con
pOggetto pari a zero, sul quale e` vietato chiamare
l'operator(), e aggiungere dunque il default ctor
    Pamero(): pOggetto(0) {}
e magari il modo di testare se un Pamero e` nullo (meglio
evitare le conversioni implicite, e.g. a bool, che danno vari
rischi e problemi, e farlo con un metodo esplicito), aggiungendo
l'ulteriore metodo inline:
    bool isNull() const { return pOggetto==0; }
Possiamo anche, agevolmente, arricchire Pamero in modo
che, in alternativa a chiamare un metodo su di un oggetto di
classe Base, possa invece chiamare una funzione non-metodo
(libera o statica), sempre con argomento double e valore di
ritorno pure double, tanto per esemplificare.  Ad esempio:
    class Pameroofl {
        Base* pOggetto;
        typedef double (Base::* pMetodo_t)(double);
        typedef double (*pFunz_t)(double);
        union {
            pMetodo_t pMetodo;
            pFunz_t pFunz;
        }
    public:
        Pameroofl(pFunz_t pF=0):
            pOggetto(0), pFunz(pF)
        {}
        Pameroofl(Base *pO, pMetodo_t pM):
            pOggetto(pO), pMetodo(pM)
        {}
        bool isNull() const { return pOggetto==0 && pFunz==0; }
        double operator()(double x) {
            return 
                pOggetto?
                (pOggetto->*pMetodo)(double):
                (*pFunz)(double);
        }
    };
Questo metodo e` perfettamente soddisfacente se sai
con precisione da quale classe (qui, Base) deriveranno
gli oggetti su cui ti interessa poter chiamare i metodi.
Se no, ci sono vari possibili arricchimenti, tipicamente
basati sull'uso di template, ed eventualmente anche su
metodi virtuali.  Vediamo, ad esempio, uno schema
del tutto generale.  Definiamo anzitutto un'interfaccia
astratta ("classe-protocollo" nella terminologia di Lakos):
    class Funtore {
    public:
        virtual double operator() double;
        // e magari, per comodita`...:
        double call(double x) { return (*this)(x); }
    };
[La call serve solo perche` non e` comodo chiamare
direttamente operatori su puntatori-a-oggetti, e, visto
che Funtore e` astratta, useremo sempre dei
puntatori-a-Funtore, quindi cosi` possiamo scrivere,
dato un Funtore* pF,
        return pF->call(x);
invece del piu` scomodo/goffo
        return (*pF)(x);
o
        return pF->operator()(x);
Comunque, e` una banale questione di "zucchero
sintattico", di importanza assai minore].
Possiamo chiaramente pensare a varie classi
concrete che implementano il "protocollo" Funtore:
    class Paf: public Funtore {
        typedef double (*pFunz_t)(double);
        pFunz_t pF;
    public:
        Paf(pFunz_t pF=0): pF(pF) {}
        double operator()(double x) { return (*pF)(x); }
    };
e
    template <class Base>
    class Pamero: public Funtore {
        Base* pOggetto;
        typedef double (Base::* pMetodo_t)(double);
        pMetodo_t pMetodo;
    public:
        Pamero(Base *pO, pMetodo_t pM):
            pOggetto(pO), pMetodo(pM)
        {}
        double operator()(double x) {
            return (pOggetto->*pMetodo)(double);
        }
    };
con le relative "funzioni-fabbrica" in overload/template:
Funtore* newFuntore(double (*pF)(double)) {
    return new Paf(pF);
}
template <class Base>
Funtore* newFuntore(Base* pO, double (Base::* pM)(double)) {
    return new Pamero<Base>(pO, pM);
}
(il vantaggio di usare come template delle funzioni e` che
esse possono dedurre automaticamente il tipo del template
sulla base del tipo dei loro argomenti attuali -- molto comodo,
in generale, anche se qui, con una sola classe base assai
facilmente individuabile, il vantaggio pratico e` modesto).
Nota che puoi pensare anche a molte altre sottoclassi concrete
di Funtore, tutte incapsulabili in modi analoghi a queste; e` dunque
una soluzione assai generale.
Una classe X che precedentemente teneva un double (*pF)(double)
puo` dunque passare a tenere un Funtore* pF con i soli
accorgimenti di settarlo con l'opportuna newFuntore, liberarlo
con delete pF quando necessario per evitare memory leak,
ecc (per ulteriore comodita` si puo` usare uno smart pointer,
o handle, di Funtore, invece del puntatore nudo).
Alex