2012-04-10 7 views
6

Diciamo che ho un gestore di classe con alcune sottoclassi come lo Stringhandler, SomeTypeHandler, AnotherTypeHandler. Il class handler definisce un metodo "handle" come un'interfaccia comune per tutte le sottoclassi. La logica di "gestire" è naturalmente completamente diversa per i diversi gestori.Come posso passare "Qualsiasi tipo di dati" a una funzione in C++

Quindi quello che devo fare è passare un valore di qualsiasi cosa al metodo handle. Le classi specifiche possono quindi trasmettere il "qualcosa" al tipo che si aspettano.

Fondamentalmente quello che serve è qualcosa di simile alla classe Object Java: D

La prima cosa che ho provato è stato un void*, ma a quanto pare non si può fare B* someB = dynamic_cast<B*>(theVoidPointer), quindi senza fortuna lì.

La mia seconda idea era usare boost::any. tuttavia, il requisito per utilizzare boost :: any è che il valore deve essere cunstructable, che non è il caso dei miei dati.

Qualche idea per far funzionare tutto questo?

Grazie

EDIT: Si noti che io so che potrei utilizzare una classe SomeData senza membri a tutti, e lasciare che i miei dati siano sottoclassi di questo, ma sto cercando un approccio più generico che non necessita di me per creare la mia classe di wrapper.

+2

Sei sicuro di aver bisogno di 'dynamic_cast', e non puoi usare' reinterpret_cast'? –

+1

Come usare i modelli? –

+0

Sembra che la maggior parte delle tue opzioni siano descritte qui: http://stackoverflow.com/questions/913505/casting-void-pointers-depending-on-data-c – briantyler

risposta

1

Va bene, ecco un approccio semplice utilizzando boost :: un altro per tenere i puntatori ai vostri tipi di dati. Tuttavia, fai attenzione a questo boost :: any aggiunge un po 'di codice di overhead che riduce leggermente le prestazioni (nella maggior parte dei casi trascurabili) .. considera l'uso di boost :: spirit :: hold_any o * void * se non hai bisogno di sicurezza del tipo.

class Handler { 
public: 
    void handle(boost::any value) { 
     do_handle(value); 
    } 
private: 
    virtual void do_handle(boost::any& value) = 0; 
}; 

template<class T> 
class HandlerBase : public Handler { 
private: 
    void do_handle(boost::any& value) { 
     // here you should check if value holds type T*... 
     handle_type(*(boost::any_cast<T*>(value))); 
    } 

    void handle_type(const T& value) = 0; 
}; 

class StringHandler : HandlerBase<std::string> { 
private: 
    void handle_type(const std::string& value) { 
     // do stuff 
    } 
}; 

Ora è possibile scrivere un sacco di classi handler, derivante da HandlerBase, senza assumere che i tipi trattati hanno una classe base comune.

+0

Questa è la soluzione che ho usato, fatta eccezione per il modello HandlerBase. L'ho lasciato fuori. Ho appena lasciato che i gestori specifici facciano il cast del tipo di cui hanno bisogno. Non appena avrò più gestori per lo stesso tipo di dati, acconsentirò a mettere in mezzo la base di modelli. Grazie! –

-1

Si può fare

B* someB = static_cast<B*>(theVoidPointer); 
+2

'statico_cast' per favore. –

+0

@ R.MartinhoFernandes e BertR Potresti per favore esporre le differenze tra reinterpretare, statico e dinamico? Pensavo che reinterpretare e statico avesse alcuni pericoli. –

+0

@ W.Goeman Vedi questa domanda precedente: http://stackoverflow.com/a/332086/46642 –

0

È necessario disporre di una classe base denominata DataObject o qualcosa del genere. Tutti i tipi di dati (stringa, numero, quant'altro) sono sottoclassi di DataObject. Definisci Gestisci in questo modo:

void Handle(DataObject *dataObject); 

Questo è un modo molto più sicuro di fare ciò che vuoi. Per renderlo ancora migliore, DataObject può persino sapere che tipo di dati contiene. Quindi i gestori possono verificare di aver ricevuto il giusto tipo di dati.

+0

Questo è fondamentalmente ciò che ho aggiunto nella mia modifica. Questa sembra davvero una buona soluzione, ma cercavo un approccio più generico. Continuo a tenerlo nel mio elenco di opzioni nel caso in cui le altre soluzioni non funzionino. –

1

È possibile ad esempio definire una classe base:

class BaseHandlerData { 
    ... 
}; 

Poi derivare la classi di dati specifici, che si prevede per i gestori:

class StringData: public BaseHandlerData { 
    ... 
}; 

class SomeData: public BaseHandlerData { 
    ... 
}; 

Poi si dovrebbe essere in grado di passare un BaseHandlerData * argomento del metodo handle e usa qualcosa del tipo:

void handle(BaseHandlerData *data) { 
    StringData* stringData = dynamic_cast<StringData*>(...); 
    // handle your string data here ... 
} 

per sicurezza cast per il tipo di dati previsti.

Gerald

+0

Questo è in dettaglio ciò che ho brevemente detto nella mia modifica. Una buona soluzione in effetti, ma preferisco non scrivere la classe extra per questo. Ho piuttosto questo come utilità di base (come boost :: any). –