Passare nome classe come argomento funzione

di il
10 risposte

Passare nome classe come argomento funzione

Ciao a tutti,
scrivo per chiedervi un aiuto
In pratica, ho una classe 'interface' che ha un metodo che deve aprire finestre diverse a seconda della classe che lo chiama. Quindi ho pensato di passare il nome della classe chiamante il metodo come argomento della funzione, in modo da riuscire ad effettuare l'azione corretta. Posto qua il codice così è più chiaro:


La classe interface, il metodo incriminato è compare (.cpp)

interface::interface(){} //Constructor

interface::~interface(){} //Destructor


int interface::x_mouse=0;
int interface::y_mouse=0;
bool interface::mouse_left_button;  //1 if it's pressed, 0 if not
std::string interface::who;


void interface::compare(float x_coord, float y_coord, int position, std::string who_is)
{
	if(x_mouse>=((x_coord*70)+350) && x_mouse<=((x_coord+0.2)*70+350)){
		if(y_mouse<=((y_coord*(-70))+200) && y_mouse>=(((y_coord+0.2)*(-70))+200)){ 
			if(mouse_left_button==1){
				opening::counter=position;
				who::m_launch();	  //L'ERRORE E' RIFERITO A QUESTA RIGA		
			}
		}
	}

}


interface.h:

class interface
{
	public:
		interface();
		~interface();

		static int x_mouse;
		static int y_mouse;
		static bool mouse_left_button;
		static std::string who;

		static void compare(float x_coord, float y_coord, int position, std::string who);
		static void getMouse(int button, int state, int x, int y); 
		static void keyPressed (unsigned char key, int x, int y);
		static void keyUp (unsigned char key, int x, int y);
		static void keySpecial (int key, int x, int y);
		static void keySpecialUp (int key, int x, int y);
		static void key_func(void);
	private:

};

La chiamata del metodo da parte di un'altra classe:

void opening::m_drawButton(float x_coord, float y_coord, int position)    //METHOD: m_drawButton
{
	
	glutMouseFunc(interface::getMouse);  //Here I call functions for interfacing with mouse
	interface::compare(x_coord, y_coord, position, "opening");

}
Compilando mi dà come errore error: ‘who’ is not a class or namespace.
Escluderei un dovuto all'allegamento del .h, in quanto tutte le classi sono definite nello stesso .h e prima di inserire questo metodo funzionava...
Sinceramente non riesco a capire cosa ci sia che non va (e qualcosa sicuramente c'è), ringrazio chiunque abbia anche solo la pazienza di leggere tutto, poi se la soluzione (o idee per la soluzione) arriva tanto meglio!

10 Risposte

  • Re: Passare nome classe come argomento funzione

    Confronta i prototipi:
    
    void interface::compare(float x_coord, float y_coord, int position, std::string who_is)
    
    static void compare(float x_coord, float y_coord, int position, std::string who);
    
    
    Non manca qualcosa?
  • Re: Passare nome classe come argomento funzione

    Se ti riferisci al fatto che uno era 'who' e l'altro 'who_is' quello di sicuro non aiutava
    Il problema però persiste... a livello teorico (lasciando stare errori vari) dovrebbe essere possibile fare quello che voglio fare io, no? Intanto grazie!
  • Re: Passare nome classe come argomento funzione

    Se ti aspetti che questo:
    
    who::m_launch();
    
    venga espanso in
    
    nome_tua_classe::m_launch
    
    rimarrai deluso. Una cosa del genere è illegale.
    Potresti risolvere con un template, polimorfismo o una factory speciale bastata su std::map<> ( quindi polimorfismo) a seconda del design, ma passare il nome di una classe nei termini in cui pensi tu, no.
  • Re: Passare nome classe come argomento funzione

    Grob, era quello che pensavo di fare...
    Ti ruberei ancora un minuto se vuoi: mi puoi per favore spiegare l'idea che sta dietro per risolvere il problema con un template e con std::map? (Tra l'altro, dubbio, map non è esso stesso un template?)
  • Re: Passare nome classe come argomento funzione

    Con un template verrebbe così:
    
    template <typename T>
    static void compare(float x_coord, float y_coord, int position, T who);
    
    template <typename T>
    void interface::compare(float x_coord, float y_coord, int position, T who)
    {
       if(x_mouse>=((x_coord*70)+350) && x_mouse<=((x_coord+0.2)*70+350)){
          if(y_mouse<=((y_coord*(-70))+200) && y_mouse>=(((y_coord+0.2)*(-70))+200)){
             if(mouse_left_button==1){
                opening::counter=position;
                T::m_launch();   // se la funzione è statica
                 // who.m_launch() se la funzione è membro.    
             }
          }
       }
    }
    
    Il vantaggio è che è semplice da gestire, lo svantaggio è che quel T può essere qualsiasi cosa (int compreso), e se manca la funzione m_launch() il compilatore da errore (si potrebbe risolvere con SFINAE se il tuo compilatore la supporta, ma significa solo introdurre complicazioni).

    Il metodo con la std::map si basa sul fatto che la tua classe dev'essere polimorfica e deve prevedere una funzione di creazione a parametri fissi.
    E' una variazione più semplice e più type safe di quanto puoi trovare qui (più o meno a metà pagina):
    http://www.eptacom.net/pubblicazioni/pub_it/nl_2.htm

    Prima di proseguire, però, che compilatore stai usando?
  • Re: Passare nome classe come argomento funzione

    Grazie mille shodan, così comincia ad essere più chiaro!
    Come compilatore uso g++.

    Avevo anche trovato un altro modo per far funzionare il metodo, ma è abbastanza laborioso e decisamente più grezzo di quello che hai consigliato: a seconda della stringa che passo metto degli if che mi indirizzano sulla finestra giusta da aprire, ieri non ci avevo pensato.
    Da quel che ho capito, utilizzando la map farei un controllo del genere a seconda della chiave che chiamo, no?

    (Ah, adesso che sto capendo come fare in modo rigoroso non userei il metodo che avevo trovato, decisamente meglio quello che consigli tu)
  • Re: Passare nome classe come argomento funzione

    Da quel che ho capito, utilizzando la map farei un controllo del genere a seconda della chiave che chiamo, no?
    Si, in pratica si una una stringa come indice per farsi restituire un puntatore alla classe contenuta. Il vantaggio è che il codice rimane bello pulito, lo svantaggio è che la map dev'essere già pronta prima di usarla (altrimenti non avrebbe senso).
    Tieni presente però che le tue finestre devono derivare come minimo da una classe base tipo questa:
    
    class WIndowBase {
        public:
            virtual ~WindowBase() {}
            virtual m_launch() {}
    };
    ...
    
    typedef WindowBase* (*CREATE_FUNCTION) ()
    map<string, CREATE_FUNCTION> windowFactory;
    
    // se usi una versione di g++ recente è meglio 
    map<string, std::function<WindowBase*()>> windowFactory;
    
    
    perché con il void* ti ritroveresti a dover fare un cast per ogni tipo non derivato, il che è inconcepibile se hai diciamo più di tre tipi di finestre.
    Con il metodo suggerito la tua funzione diventa:
    
    void interface::compare(float x_coord, float y_coord, int position, std::string who)
    {
       if(x_mouse>=((x_coord*70)+350) && x_mouse<=((x_coord+0.2)*70+350)){
          if(y_mouse<=((y_coord*(-70))+200) && y_mouse>=(((y_coord+0.2)*(-70))+200)){
             if(mouse_left_button==1){
                opening::counter=position;
                // assumento che la map<string, WindowBase* (*)() > si chiami windowFactory
                std::unique_ptr<WindowBase> obj( windowFactory[who]() );
                obj->m_launch();   
             }
          }
       }
    }
    
    Dato che non hai fornito notizie riguardo il lifetime della tua finestra ho ristretto lo scope arbitrariamente. Tieni presente però che il meccanismo può essere variato a piacere (dipende da quando vuoi cancellare la tua finestra).
  • Re: Passare nome classe come argomento funzione

    Ciao, grazie ancora per la pazienza
    Allora, le finestre devono rimanere aperte finchè non viene dato il comando di chiusura dall'utente, quindi se nessuno fa niente rimangono lì.
    Uhm, il metodo con la map vedrò di studiarlo bene...

    Nel frattempo ho provato con il template, ma c'è qualcosa che non mi torna, questo è il codice:


    .cpp
    
    int opening::counter=1; //I need this for changing the colour while selecting option in main menu
    
    opening::opening(){}  //Constructor 
    opening::~opening(){} //Destructor
    
    void opening::m_drawButton(float x_coord, float y_coord, int position)    //METHOD: m_drawButton
    {
    	glutMouseFunc(interface::getMouse);  //Here I call functions for interfacing with mouse
    	interface::compare(x_coord, y_coord, position, "opening");
    }
    
    
    
    
    //******************INTERFACE**********************
    
    interface::interface(){} //Constructor
    
    interface::~interface(){} //Destructor
    
    
    int interface::x_mouse=0;
    int interface::y_mouse=0;
    bool interface::mouse_left_button;  //1 if it's pressed, 0 if not
    
    
    template <typename T>
    void interface::compare(float x_coord, float y_coord, int position, T who)
    {
    	if(x_mouse>=((x_coord*70)+350) && x_mouse<=((x_coord+0.2)*70+350)){
    		if(y_mouse<=((y_coord*(-70))+200) && y_mouse>=(((y_coord+0.2)*(-70))+200)){  
    			if(mouse_left_button==1){
    				opening::counter=position;
    				T::m_launch();			
    			}
    		}
    	}
    
    }
    
    .h
    
    class opening
    {
    	friend class interface; //This is to allow interface to access m_launch 
    	public:
    		opening();
    		~opening();
    		static int counter;
    		static void reshape (int width, int height);
    		static void display (void);		
    	protected:
    		static void m_drawOption (float x_coord, float y_coord, int position, std::string option);
    	private:
    		static void m_launch ();
    	
    };
    
    
    
    
    class interface
    {
    	public:
    		interface();
    		~interface();
    
    		static int x_mouse;
    		static int y_mouse;
    		static bool mouse_left_button;
    			
    		template <typename T>
    		static void compare(float x_coord, float y_coord, int position, T who);
    		static void getMouse(int button, int state, int x, int y); 
    	private:
    
    };
    
    
    Mi sembra di aver seguito abbastanza le tue indicazioni, ma mi dà come errore ‘m_launch’ is not a member of ‘const char*’, il che se ci penso bene è abbastanza logico stando a quel poco che so di template, in quanto l'argomento che gli passo è proprio di quel tipo. A questo punto mi sa che non ho capito come fare a fargli identificare la classe che effettua la richiesta, così mi sembra che lui stia cercando una classe che si chiama 'const char'...


    Ah, se al posto di
    T::m_launch
    uso
    who.m_launch()
    ottengo request for member ‘m_launch’ in ‘who’, which is of non-class type ‘const char*’
  • Re: Passare nome classe come argomento funzione

    Mi auto quoto.
    Il vantaggio è che è semplice da gestire, lo svantaggio è che quel T può essere qualsiasi cosa (int compreso), e se manca la funzione m_launch() il compilatore da errore (si potrebbe risolvere con SFINAE se il tuo compilatore la supporta, ma significa solo introdurre complicazioni).
    e non esiste nessuna classe "const char".
    const char significa un carattere che non si può modificare, const char* una stringa ASCIIZ che non si può modificare.
    Il parametro del template dev'essere un oggetto di classe finestra, non una string o ASCIIZ string.
    
    ClassFinestra cf;
    obj.compare(10.0, 4.0, 70, cf );
    
    Il parametro cf serve solo a far desumere al compilatore il tipo della tua finestra, l'alternativa è esplicitare il parametro del template, ma non è molto pratico.
    Ad esempio:
    
    ClassFinestra cf;
    obj.compare<ClassFinestra>(10.0, 4.0, 70 ); // no ultimo parametro.
    
    Se non hai confidenza con i template, lasciali stare al momento (anche perché non puoi mettere una funzione template in un file .cpp o il linker protesta). Personalmente io non userei un template in questo caso, ma il metodo della map<> (o addirittura un vector<> con un'oppotuna enum come chiave).
    P.S.
    Giusto per parlare la stessa lingua, cosa sai del polimorfismo (anche a grandi linee)?
  • Re: Passare nome classe come argomento funzione

    Eh, purtroppo del polimorfismo so poco, è uno di quegli argomenti su cui mi sto cimentando (li affronto un po' all'occorrenza...).
    Vedrò di studiare un po' il funzionamento del metodo della map che hai proposto (o anche il vector è interessante), in questi giorni in cui non avrò internet a disposizione (almeno faccio fruttare il tempo), poi ti dico come procede
    Grazie mille delle dritte che hai dato, sono utilissime!
Devi accedere o registrarti per scrivere nel forum
10 risposte