2012-12-29 7 views
14

Prima di tutto voglio dire che il seguente ha funzionato bene fino a Qt 5.0.0 beta 1 (forse anche beta 2 e RC, non so), ma fallisce in Qt 5.0.0 versione finale. Voglio solo fare riferimento ai risultati visti nella versione finale di Qt 5.0.0. Quindi molto probabilmente questo ha qualcosa a che fare con i recenti cambiamenti in Qt5.Usa C++ - slot in QML che restituisce il tipo nello spazio dei nomi

Sul lato C++ ho un set di classi (derivate da QObject) in un namespace (che è attivato facoltativamente con i flag del compilatore, le classi si trovano in una libreria separata e la libreria lascia l'uso di un namespace come opzione per l'utente della biblioteca). Una classe, qui Game, potrebbe assomigliare a questo (estratto):

OAE_BEGIN_NAMESPACE 

// forward-declarations: 
class Player; // Player is just another class in the same library 

class Game : public QObject 
{ 
    Q_OBJECT 

public: 
    explicit Game(...); 

public slots: 
    Player *player() const; // <-- the quesion is about such slots 
}; 

OAE_END_NAMESPACE 

Le macro OAE_BEGIN/END_NAMESPACE espandersi a uno namespace OAE_NAMESPACE { ... } o nulla, nello stesso modo in Qt fa in <qglobal.h>, solo "QT" sostituito con "OAE" nei nomi delle macro:

#ifndef OAE_NAMESPACE 

# define OAE_PREPEND_NAMESPACE(name) ::name 
# define OAE_USE_NAMESPACE 
# define OAE_BEGIN_NAMESPACE 
# define OAE_END_NAMESPACE 
# define OAE_BEGIN_INCLUDE_NAMESPACE 
# define OAE_END_INCLUDE_NAMESPACE 
# define OAE_BEGIN_MOC_NAMESPACE 
# define OAE_END_MOC_NAMESPACE 
# define OAE_FORWARD_DECLARE_CLASS(name) class name; 
# define OAE_FORWARD_DECLARE_STRUCT(name) struct name; 
# define OAE_MANGLE_NAMESPACE(name) name 

#else /* user namespace */ 

# define OAE_PREPEND_NAMESPACE(name) ::OAE_NAMESPACE::name 
# define OAE_USE_NAMESPACE using namespace ::OAE_NAMESPACE; 
# define OAE_BEGIN_NAMESPACE namespace OAE_NAMESPACE { 
# define OAE_END_NAMESPACE } 
# define OAE_BEGIN_INCLUDE_NAMESPACE } 
# define OAE_END_INCLUDE_NAMESPACE namespace OAE_NAMESPACE { 
# define OAE_BEGIN_MOC_NAMESPACE OAE_USE_NAMESPACE 
# define OAE_END_MOC_NAMESPACE 
# define OAE_FORWARD_DECLARE_CLASS(name) \ 
    OAE_BEGIN_NAMESPACE class name; OAE_END_NAMESPACE \ 
    using OAE_PREPEND_NAMESPACE(name); 

# define OAE_FORWARD_DECLARE_STRUCT(name) \ 
    OAE_BEGIN_NAMESPACE struct name; OAE_END_NAMESPACE \ 
    using OAE_PREPEND_NAMESPACE(name); 

# define OAE_MANGLE_NAMESPACE0(x) x 
# define OAE_MANGLE_NAMESPACE1(a, b) a##_##b 
# define OAE_MANGLE_NAMESPACE2(a, b) OAE_MANGLE_NAMESPACE1(a,b) 
# define OAE_MANGLE_NAMESPACE(name) OAE_MANGLE_NAMESPACE2(\ 
     OAE_MANGLE_NAMESPACE0(name), OAE_MANGLE_NAMESPACE0(OAE_NAMESPACE)) 

namespace OAE_NAMESPACE {} 

# ifndef OAE_BOOTSTRAPPED 
# ifndef OAE_NO_USING_NAMESPACE 
    /* 
    This expands to a "using OAE_NAMESPACE" also in _header files_. 
    It is the only way the feature can be used without too much 
    pain, but if people _really_ do not want it they can add 
    DEFINES += OAE_NO_USING_NAMESPACE to their .pro files. 
    */ 
    OAE_USE_NAMESPACE 
# endif 
# endif 

#endif /* user namespace */ 

in seguito, quando dice "che permette lo spazio dei nomi", voglio dire ho dichiarato la macro OAE_NAMESPACE, in questo caso con il valore oae.

Accedo ad altre istanze di questa classe e alla classe Player restituita da player() all'interno di QML per l'interfaccia utente della mia applicazione. Per questo, registro le classi come segue:

qmlRegisterType<Game>(); 
qmlRegisterType<Player>(); 

fornisco il QML frontend un puntatore a un'istanza di un Game, chiamato theGame entro QML:

view.engine()->rootContext()->setContextProperty("theGame", 
     QVariant::fromValue<Game*>(game)); 

Da QML, uso questo come solito.Un piccolo esempio dovrebbe stampare un indirizzo di puntatore del player():

Rectangle { 
    width: 100; height: 100 
    Component.onCompleted: console.log(theGame.player()) 
} 

vengo i seguenti risultati, a seconda se ho impostato un OAE_NAMESPACE o no (a proposito: io uso la stessa impostazione sia per la biblioteca e la applicazione di usarlo):

  • Quando disattivando lo spazio dei nomi, tutto funziona come previsto e QML mi stampa il puntatore:

    Player(0x10b4ae0) 
    
  • Quando permettendo lo spazio dei nomi (e using nel codice C++ utilizzando la libreria, in modo da non modificare il codice a tutti), QML non riesce a capire il tipo di ritorno di Game::player():

    Error: Unknown method return type: Player* 
    
  • Quando cambiando il tipo di ritorno di Game::player() a oae::Player*, tutto funziona ancora benissimo:

    oae::Player(0x10b4ae0) 
    

mia conclusione finora è che moc non considera lo spazio dei nomi ho messo in giro la classe. La mia prima risposta è stata: Ehi, moc non sa che io definisco lo spazio dei nomi quando si chiama g++, che è quello che faccio nel file .pro:

DEFINES += OAE_NAMESPACE=oae 

Tuttavia, quando si cambia il tipo di ritorno per OAE_NAMESPACE::Player*, si funziona ancora, in modo da moc fa conoscenza del OAE_NAMESPACE macro, ma non né espandere i OAE_BEGIN/END_NAMESPACE macro o non analizza i namespace affatto più.

moc produce il seguente "StringData" per Player * Game::player() const che containes tipo di ritorno del metodo:

  • Quando disattivando lo spazio dei nomi e utilizzando il tipo di ritorno Player*:

    "player\0Player*\0" 
    
  • Quando permettendo lo spazio dei nomi e utilizzando il tipo di ritorno Player*:

    "player\0Player*\0" 
    
  • Quando permettendo lo spazio dei nomi e utilizzando il tipo di ritorno OAE_NAMESPACE::Player*:

    "player\0oae::Player*\0" 
    

D'altra parte, moc antepone i nomi delle classi come restituito dal QMetaObject::className() con lo spazio dei nomi, se abilitato.

La mia conclusione è che ora ho potuto risolvere questo problema scrivendo OAE_NAMESPACE::ClassName invece di ClassName ogni volta che l'utilizzo di questi tipi nelle firme di meta metodi QObject. (Bene, c'è la macro migliore OAE_PREPEND_NAMESPACE). Dal momento che questo sembrerebbe orribile nel codice, e per me sembra anche sbagliato perché il metodo è già nel namespace, c'è una soluzione migliore?

Ora c'è anche OAE_BEGIN/END_MOC_NAMESPACE (analogo a QT_BEGIN/END_MOC_NAMESPACE), così forse ho bisogno di quelle ovunque? Non so dove/come vengono utilizzati in Qt, quindi dovrei usarli di conseguenza nella mia libreria, dal momento che voglio usare la stessa funzionalità di namespace opzionale come fa Qt.

+0

Aggiungerò alcune informazioni su quale codice produce codice (come è stata analizzata la firma dello slot, ad esempio). – leemes

+1

L'utilizzo di qmlRegisterType <> con un nome di dominio qualificato per lo spazio dei nomi cambia qualcosa? – mlvljr

+0

@mlvjr Grazie per il tuo suggerimento, ma ho già provato questo. In realtà, questo non dovrebbe cambiare nulla, dal momento che è un identificatore di tipo C++, ma si tratta di come 'moc' analizza il file di intestazione. Non rileva che 'Player' all'interno del namespace fa riferimento alla classe' oae :: Player' quando viene usata al di fuori dello spazio dei nomi. Inoltre, credo che questo comportamento fosse diverso in Qt 5.0.0 alfa e beta1. – leemes

risposta

8

Ha funzionato davvero in 5.0.0a?

Ho sfogliato il codice sorgente Qt 5.0.0 e ho cercato dove vengono analizzati i metodi, in particolare il tipo restituito (fyi, 5.0.0 \ qtbase \ src \ tools \ moc \ moc.cpp: L160) e non ci sono namespace controllare (né sugli argomenti, quindi player(Player* p) non funzionerà neanche). considerando che è fatto per la definizione della classe (5.0.0 \ qtbase \ src \ tools \ moc \ moc.cpp: L620 & L635)

penso "noi" possiamo chiamare questo un bug (o una svista)

+0

Grazie per il vostro impegno. Controllerò questo domani. Ovviamente l'errore può essere dalla mia parte, non voglio escludere questa possibilità;) – leemes

+0

Bene, sembra * non * che funzioni anche in Qt 5.0.0 beta1. L'ho appena provato. Strano che ricordassi che funzionava. Grazie per aver dedicato del tempo a trovare questa roba nel codice Moc e congratulazioni per i tuoi punti di merito! – leemes

Problemi correlati