Modelli per la gestione dei set di proprietà modificabili in C ++

3

Ho un sacco "Property Set" (che sono semplici structs contenenti membri POD). Vorrei modificare questi set di proprietà (ad es .: aggiungere un nuovo membro) in fase di esecuzione in modo che la definizione dei set di proprietà possa essere esternalizzata e il codice stesso possa essere riutilizzato con più versioni / tipi di insiemi di proprietà con il minimo /nessun cambiamento.

Ad esempio, un insieme di proprietà potrebbe assomigliare a questo:

struct PropSetA
{
    bool activeFlag;
    int processingCount;
    /* snip few other such fields*/
};

Ma invece di impostare la sua definizione in pietra al momento della compilazione, mi piacerebbe crearlo dinamicamente in fase di esecuzione. Qualcosa come:

class PropSet propSetA;
propSetA("activeFlag",true);  //overloading the function call operator
propSetA("processingCount",0); 

E il codice dipendente dai set di proprietà (eventualmente in qualche altra libreria) userà i dati in questo modo:

 bool actvFlag = propSet["activeFlag"];
 if(actvFlag  == true)
 {
   //Do Stuff
 }

L'attuale implementazione dietro a tutto questo è la seguente:

class PropValue
{
 public:
    // Variant like class for holding multiple data-types
    // overloaded Conversion operator. Eg:
    operator bool()
    {
      return (baseType == BOOLEAN) ? this->ToBoolean() : false;
    }
    // And a method to create PropValues various base datatypes
    static FromBool(bool baseValue);
};

class PropSet
{

 public:
  // overloaded[] operator for adding properties
  void operator()(std::string propName, bool propVal)
  {
    propMap.insert(std::make_pair(propName, PropVal::FromBool(propVal)));
  }

 protected:
   // the property map
   std::map<std::string, PropValue> propMap;
};

Questo problema è simile alla domanda su SO e all'approccio attuale (descritto sopra) si basa su questa risposta. Ma come notato sopra a SO questo è più di un hack che una soluzione adeguata. I problemi fondamentali che ho con questo approccio sono i seguenti:

  • Estendere questo supporto per supportare nuovi tipi richiederà un significativo cambio di codice. Al minimo indispensabile, gli operatori sovraccaricati devono essere estesi per supportare il nuovo tipo.
  • Il supporto di proprietà complesse (ad esempio struct contenente struct ) è complicato.
  • Supportare un meccanismo di riferimento (necessario per un'ottimizzazione della non duplicazione di set di proprietà identiche) è complicato. Questo vale anche per supportare puntatori e array multidimensionali in generale.

Esistono modelli noti per gestire questo scenario? In sostanza, sto cercando l'equivalente del modello visitatore , ma per estendere le proprietà class anziché i metodi.

Modifica: Dichiarazione di problemi modificata per chiarezza e aggiunta un po 'di codice dall'attuale implementazione.

    
posta Bhargav Bhat 18.04.2012 - 08:07
fonte

3 risposte

2

Bene, AFAIU vuoi un qualche tipo di riflessione, ma per mantenere il tipo statico di controllo i dati di riflessione devono essere disponibili per i metaprogrammi del modello. Ci sono (o almeno c'erano) diversi sforzi per implementare tale libreria: Riflessione Mirror C ++ utilità , librerie di boost per la programmazione riflessiva (sembrano obsolete). Dai un'occhiata a loro almeno come fonte di ispirazione. Dai anche un'occhiata a Boost.Fusion , è una libreria per lavorare con raccolte eterogenee di dati (tuple), che a loro volta possono rappresentare strutture.

La riflessione statica più semplice può essere ottenuta utilizzando un generatore di codice separato che produce un codice come:

struct Foo {
  int a;
  double b;
};

// Types to identify struct members
namespace name_tags { struct a; struct b; }

// Reflection metadata for struct Foo
template<>
struct DataMembers<Foo> {
  typedef boost::fusion::result_of::make_map<
    name_tags::a,
    name_tags::b,
    Member<Foo, int>,
    Member<Foo, double>
  >::type type;

  type value;
};

DataMembers<Foo>::type
DataMembers<Foo>::value = 
  boost::fusion::result_of::make_map<
    name_tags::a,
    name_tags::b>(Member<Foo, int>(&Foo::a, "a"), 
                  Member<Foo, double>(&Foo::b, "b"));

Dove il modello di classe Member è simile a:

template<typename S, typename M>
struct Member
{
  M S::* ptr;
  std::string name;

  Member(M S::* ptr, const std::string& name) : ptr(ptr), name(name) { }
};

Quindi puoi utilizzare Boost.Fusion per applicare un functor a tutti i membri della classe. Puoi utilizzare metafunzioni aggiuntive per contrassegnare alcuni membri, ad es. come proprietà da elaborare.

L'iterazione dei membri di Foo può essere eseguita come

boost::fusion::for_each(DataMembers<Foo>::value, ProcessMember());

struct ProcessMember
{
  void operator() (const boost::fusion::pair<name_tags::a, Member<Foo, int>>& mem) const
   {
     // ...
   }

  void operator() (const boost::fusion::pair<name_tags::b, Member<Foo, double>>& mem) const
   {
     // ...
   }

   // You can also provide a default case
   template<typename T>
   void operator() (const T&) const { }
};

I tipi di tag name_tags::a e name_tags::b vengono visualizzati nelle firme di operator() e consentono di fornire una funzione di elaborazione separata per ogni membro di Foo . Se aggiungi un membro a Foo e aggiorni i dati di riflessione (questo dovrebbe essere automatizzato) e dimentichi di aggiornare l'elaborazione, l'applicazione di tali funzioni non verrà compilata.

    
risposta data 18.04.2012 - 11:13
fonte
0

Un suggerimento è di rendere un PropValue un tipo base e BoolValue, IntValue, DoubleValue, ArrayValue, possono essere classi derivate. I numeri potrebbero essere raggruppati in NumberValue e ArrayValue potrebbe essere sottoclasse in VectorValue, DictionaryValue , ecc.

Ciò potrebbe aiutare a dividere i problemi di definizione dei tratti e di dove consentire conversioni di tipi.

Non vedo un'immagine completa di dove gestisci i tipi dinamici. Sembra che il consumatore sappia o si aspetti un bool nel tuo esempio. Quindi solo lo zucchero sintattico fa una conversione sovraccaricata piuttosto che chiamare qualcosa del tipo:

bool b_by_ref;
Error err  = Propset.GetBool("propname",&b_by_ref);

Se non conosci o non ti interessa ancora del tipo, passalo come PropValue * e lascia che le funzioni si preoccupino dei tipi internamente in quell'algoritmo.

void FooFunc (PropValue* p)
{
    DWORD id = p->GetTypeID();
    switch(id)
    {
        case INT_VALUE_TYPEID:   FooInt(p);
        case FLOAT_VALUE_TYPEID: FooFloat(p);
         .....
        default: Assert(0);
    }
 }
    
risposta data 02.08.2012 - 05:51
fonte
-2

Devi avere un linguaggio dinamico reale, come Lua o Python, per gestire questo tipo di carico di lavoro.

    
risposta data 17.06.2012 - 13:38
fonte

Leggi altre domande sui tag