2015-08-23 9 views
6

In genere, un sacco di codice non fa altro che ottenere/impostare i membri della classe. Per questo ho implementato una semplice classe contenitore per avere getter e setter associati a in un "campo". A prima vista questo sembra abbastanza ok e si traduce in codice molto meno. Questo è come la classe del contenitore si presenta come:È possibile implementare accessi Q_PROPERTY READ/WRITE senza utilizzare le funzioni membro?

Member.h

#include <functional> 

template <class T> 
class Member 
{ 
public: 
    T data; 

    using Getter_t = std::function<T(void)>; 
    using Setter_t = std::function<void(T)>; 
    using Notify_t = std::function<void(void)>; 

    Setter_t m_setterFunc; 
    Getter_t m_getterFunc; 
    Notify_t m_notifyFunc; 

    Member() 
    { 
     this->m_getterFunc = [=] (void) -> T { return this->data; }; 
     this->m_setterFunc = [=] (T data) -> void { this->data = data; }; 
     this->m_notifyFunc = [] (void) -> void { }; 
    } 

    auto get() -> T { return this->m_getterFunc(); } 
    auto set(T data) -> void { this->m_setterFunc(data); this->m_notifyFunc(); } 

    auto getter(Getter_t func) -> Member& { this->m_getterFunc = func; return *this; } 
    auto setter(Setter_t func) -> Member& { this->m_setterFunc = func; return *this; } 
    auto notify(Notify_t func) -> Member& { this->m_notifyFunc = func; return *this; } 

    ~Member() { } 
}; 

So che alcune cose non sono ancora perfetto, ma va bene per ora. Le righe successive mostrano come sono definite le istanze Member e il modo semplice e conveniente per accedere ai dati sottostanti. Le funzioni get, set e notify possono essere sostituite da lambda o puntatori di funzioni per sovrascrivere il comportamento personalizzato.

main.cpp

#include <iostream> 

#include "Member.h" 

class MyClass 
{ 
public: 
    Member<int> foo; 
    Member<std::string> bar; 

    void barChanged() { std::cout << "bar changed\n"; } 
}; 

auto main(int argc, const char * argv[]) -> int 
{ 
    MyClass instance; 

    instance.foo.notify([]() -> void { std::cout << "foo changed\n"; }); 
    instance.bar.notify(std::bind(&MyClass::barChanged, instance)); 

    instance.foo.set(10); 
    instance.bar.set("some string"); 

    std::cout << instance.foo.get() << " " << instance.bar.get() << std::endl; 

    return 0; 
} 

Il problema ora è che la macro Q_PROPERTY si aspetta nomi di funzione per i READ e WRITE di accesso e sono tornato dove ho iniziato: devo scrivere ottenere e impostare funzioni per ogni proprietà in modo esplicito. Esattamente quello che volevo evitare.

class MyOtherClass : public QObject 
{ 
    Q_OBJECT 
    Q_PROPERTY(bool flag READ getFlag WRITE setFlag NOTIFY flagChanged); 
public: 
    Member<bool> m_flag; 
    auto getFlag() -> bool { return m_flag.get(); } 
    auto setFlag(bool flag) -> void { this->m_flag.set(flag); } 
}; 

E 'possibile utilizzare direttamente i già esistenti m_flag.get e m_flag.set funzioni? Ho provato le cose ovvie ma sono state respinte dal moc o hanno causato troppi codici.

Modifica

Come indicato di seguito, la parola chiave MEMBER permette di avere proprietà senza specificare get e set funzioni. Tuttavia, è possibile accedere ai membri privati ​​solo con il loro nome (this->property("myPropertyName")) e inoltre non è possibile ottenere più di "semplici" get e set.

Per renderlo più chiaro: La motivazione non è quello di evitare proprio la scrittura ottenere e funzioni di set, ma cercando di implementare un sistema di utente flessibile che

  • da esegue di default get/set come previsto
  • supporti personalizzati logico (ad esempio avanti recente valori impostato un altro esempio)
  • possono essere utilizzati per i membri della classe C++ ed è compatibile con proprietà Qt

e il su il pezzo mancante è il ponte tra gli accessor Q_PROPERTYREAD/WRITE ei metodi get/set della classe Member.

Grazie per qualsiasi aiuto!

+0

Forse dovresti provare un po 'di magia con i macro? Riunisci una macro 'Q_PROPERTY' a un'altra che creerà 2 metodi + dichiara una proprietà. Non sono sicuro, ma potrebbe non essere compatibile con il compilatore MOC. –

+0

In alternativa: consente di creare proprietà dinamiche in fase di runtime. –

+0

@SaZ Cercherò di evitare le macro nel miglior modo possibile. Ma le proprietà dinamiche potrebbero essere un'opzione, ci proverò. Grazie per il suggerimento! – qCring

risposta

6

Non credo che sia possibile reindirizzare READ o WRITE metodi di proprietà per qualche altro oggetto interno o esterno senza scrivere wrapper, ma se i vostri getter e setter non fanno nulla, tranne il ritorno o impostare i dati: non c'è MEMBER variabile associazione almeno nelle ultime versioni di Qt.

Da Qt Doc:

Q_PROPERTY(type name 
      (READ getFunction [WRITE setFunction] | 
      MEMBER memberName [(READ getFunction | WRITE setFunction)]) 
      [RESET resetFunction] 
      [NOTIFY notifySignal] 
      [REVISION int] 
      [DESIGNABLE bool] 
      [SCRIPTABLE bool] 
      [STORED bool] 
      [USER bool] 
      [CONSTANT] 
      [FINAL]) 

una lettura di accesso funzione è necessaria se non variabile membro era specificato. È per la lettura del valore della proprietà. Idealmente, una funzione const viene utilizzata per questo scopo e deve restituire il tipo di proprietà o un riferimento const a quel tipo. ad es., QWidget :: focus è una proprietà di sola lettura con funzione READ, QWidget :: hasFocus().

Una funzione di accesso WRITE è facoltativa. È per l'impostazione del valore di proprietà . Deve restituire void e deve prendere esattamente un argomento, o del tipo di proprietà o un puntatore o riferimento a quel tipo. ad esempio, QWidget :: enabled ha la funzione WRITE QWidget :: setEnabled(). Le proprietà di sola lettura non richiedono le funzioni WRITE. ad esempio, QWidget :: focus non ha alcuna funzione WRITE.

È richiesta un'associazione di variabili MEMBER se non è specificata alcuna funzione di accesso READ . Questo rende leggibile la variabile membro data e scrivibile senza la necessità di creare funzioni di accesso READ e WRITE . È ancora possibile utilizzare le funzioni di accesso READ o WRITE oltre all'associazione di variabili MEMBER (ma non entrambe), se è necessario per controllare l'accesso variabile.

Utilizzando MEMBER non è necessario scrivere getter e setter.

Problemi correlati