Domanda

Esiste un modo per aggiungere un campo a una classe in fase di esecuzione (un campo che non esisteva prima)? Qualcosa come questo frammento:

Myobject *ob; // create an object
ob->addField("newField",44); // we add the field to the class and we assign an initial value to it
printf("%d",ob->newField); // now we can access that field

Non mi interessa davvero come sarebbe fatto, non mi importa se è un brutto hack o no, vorrei sapere se poteva essere fatto, e un piccolo esempio, se possibile.

Un altro esempio: dire che ho un file XML che descrive questa classe:

<class name="MyClass">
   <member name="field1" />
   <member name="field2" />
</class>

e voglio " aggiungere " i campi "campo1" e "campo2" alla classe (supponendo che la classe esista già). Diciamo che questo è il codice per la classe:

class MyClass {
};

Non voglio creare una classe in fase di esecuzione, voglio solo aggiungere membri / campi a una esistente.

Grazie!

È stato utile?

Soluzione

Utilizza una mappa e una variante.

Ad esempio, usando boost :: variant. Vedi http://www.boost.org/doc/libs/ 1_36_0 / doc / html / variant.html

(Ma ovviamente puoi crearne uno tuo, per adattarlo ai tipi dei tuoi attributi XML.)

#include <map>
#include <boost/variant.hpp>

typedef boost::variant< int, std::string > MyValue ;
typedef std::map<std::string, MyValue> MyValueMap ;

Aggiungendo MyValueMap come membro della tua classe, puoi aggiungere proprietà in base al loro nome. Il che significa che il codice:

oMyValueMap.insert(std::make_pair("newField", 44)) ;
oMyValueMap.insert(std::make_pair("newField2", "Hello World")) ;
std::cout << oMyValueMap["newField"] ;
std::cout << oMyValueMap["newField2"] ;

Incapsulandolo in una classe MyObject e aggiungendo i giusti accessori sovraccaricati in questa classe MyObject, il codice sopra diventa un po 'più chiaro:

oMyObject.addField("newField", 44) ;
oMyObject.addField("newField2", "Hello World") ;
std::cout << oMyObject["newField"] ;
std::cout << oMyObject["newField2"] ;

Ma perdi in qualche modo la sicurezza del tipo di C ++. Ma per XML, questo è inevitabile, immagino.

Altri suggerimenti

Non c'è modo di farlo nel modo descritto, poiché il compilatore deve risolvere il riferimento al momento della compilazione, genererà un errore.

Ma vedi The Universal Design Pattern .

Non puoi far funzionare quella sintassi (a causa del controllo statico in fase di compilazione), ma se sei disposto a modificare la sintassi, puoi ottenere lo stesso effetto abbastanza facilmente. Sarebbe abbastanza facile avere un membro del dizionario con un mapping BLOB string- > e avere funzioni membro come:

template< typename T > T get_member( string name );
template< typename T > void set_member( string name, T value );

Se lo desideri, puoi rendere la sintassi più compatta / complicata (ad es. usando un override dell'operatore '- >'). Esistono anche alcuni trucchi specifici del compilatore che potresti eventualmente sfruttare (MSVC supporta __declspec (proprietà), ad esempio, che ti consente di mappare i riferimenti a una variabile membro su metodi di un formato specifico). Alla fine, però, non sarai in grado di fare qualcosa che il compilatore non accetta nella lingua e farlo compilare.

Versione breve: non posso farlo. Non esiste un supporto nativo per questo, c ++ è tipizzato staticamente e il compilatore deve conoscere la struttura di ciascun oggetto da manipolare.

Raccomandazione: utilizzare un interprete incorporato. E non scrivere il tuo (vedi sotto), prendine uno che funzioni e abbia già eseguito il debug.


Che cosa puoi fare: implementa l'interperter sufficiente per le tue esigenze.

Sarebbe abbastanza semplice configurare la classe con un membro di dati come

std::vector<void*> extra_data;

a cui è possibile allegare dati arbitrari in fase di esecuzione. Il costo di questo è che dovrai gestire quei dati a mano con metodi come:

size_t add_data_link(void *p); // points to existing data, returns index
size_t add_data_copy(void *p, size_t s) // copies data (dispose at
                                        // destruction time!), returns 
                                        // index 
void* get_data(size_t i); //...

Ma questo non è il limite, con un po 'più di attenzione, potresti associare i dati arbitrari a un nome e puoi continuare a elaborare questo schema per quanto desideri (aggiungi informazioni sul tipo, ecc ...), ma ciò a cui si riduce è l'implementazione di un interprete per prendersi cura della tua flessibilità di runtime.

No - C ++ non supporta alcuna manipolazione del sistema di tipi come questo. Anche i linguaggi con un certo grado di riflessione sul runtime (ad es. .NET) non supportano esattamente questo paradigma. Avresti bisogno di un linguaggio molto più dinamico per poterlo fare.

Stavo guardando questo e ho fatto una piccola ricerca in giro, questo frammento di codice ottenuto da: Blog di Michael Hammer sembra essere un buon modo per farlo, usando boost :: qualsiasi

Per prima cosa definisci una struttura che definisce una std :: map che contiene una chiave (cioè il nome della variabile) e il valore. Una funzione è definita per annunciare la coppia e impostarla insieme a una funzione per ottenere il valore. Abbastanza semplice se me lo chiedi, ma sembra un buon modo per iniziare prima di fare cose più complesse.

struct AnyMap {
  void addAnyPair( const std::string& key , boost::any& value );

  template<typename T>
  T& get( const std::string key ) {
    return( boost::any_cast<T&>(map_[key]) );
  }

  std::map<const std::string, boost::any> map_;
};

void AnyMap::addAnyPair( const std::string& key , boost::any& value ) {
  map_.insert( std::make_pair( key, value ) );
}

In conclusione, questo è un trucco, dato che C ++ è un linguaggio di controllo del tipo rigoroso, e quindi i mostri si trovano dentro per quelli che piegano le regole.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top