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 diGame::player()
:Error: Unknown method return type: Player*
Quando cambiando il tipo di ritorno di
Game::player()
aoae::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.
Aggiungerò alcune informazioni su quale codice produce codice (come è stata analizzata la firma dello slot, ad esempio). – leemes
L'utilizzo di qmlRegisterType <> con un nome di dominio qualificato per lo spazio dei nomi cambia qualcosa? – mlvljr
@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