2013-04-16 13 views
6

Um eine Fallunterscheidung für einen Parameter zu machen t vom Typ T SFINAE verwenden, möchte ich, wenn die Erklärung wissenÜberprüfen Sie, ob Typ als Meta-Typ-System deklariert wird (für SFINAE)

QVariant::fromValue(t); 

und/oder

QVariant::value<T>(); 

kompiliert. Wenn das eine kompiliert, tut das andere auch, es sei denn, Sie hacken das Meta-Typ-System. Sie kompilieren genau dann, wenn T mit Q_DECLARE_METATYPE(T) deklariert wurde.

Sehr einfaches Anwendungsbeispiel, wo man den Typ eines Wertes einfach durch qDebugging eines variantenumsponnenen Äquivalents drucken möchte, wenn und nur wenn vom Metatypsystem unterstützt (Ich brauche das nicht, aber das zeigt das Problem in einem minimalen Beispiel):

template<class T> // enable if T NOT registered in the Qt meta type system 
void print(const T &t) { 
    qDebug() << t; 
} 

template<class T> // enable if T registered in the Qt meta type system 
void print(const T &t) { 
    qDebug() << QVariant::fromValue<T>(); 
} 

ich kenne einige (noch ähnlich) Möglichkeiten, dies zu tun, aber alle von ihnen einige Helfern structs vorstellen, komplizierte enable_if usw. Jetzt weiß ich, dass es QTypeInfo ist das, wie ich glaube, , bietet bereits so etwas wie ein Typ "ist in der Qt-Meta-Typ-System deklariert" -Typ. Diese Klasse ist jedoch nicht dokumentiert und daher wird sie nicht für die Verwendung in Langzeit- und Produktivcode empfohlen, da sie sich zwischen Qt-Versionen ändern kann.

Gibt es einen sehr einfachen Weg (einfacher als mit einem "checker" + enable_if), um eine SFINAE Spezialisierung einzutragen, wenn ein Typ T von QVariant unterstützt wird?

Bitte beachten Sie, dass die Lösung immer Portable zwischen verschiedenen Qt-Versionen (Qt4 und Qt5 könnte eine andere QTypeInfo Definition verwenden). Allerdings verwende ich C++ 11, also habe ich beispielsweise Zugriff auf std::enable_if.

Der "nicht portable" Weg ist, die interne Definition von QMetaTypeId2<T>::Defined in einem enable_if (es ist ein Enum-Wert als entweder 0 oder 1 definiert) zu verwenden. Somit würde eine Arbeitslösung sein:

template<class T> 
typename std::enable_if<!QMetaTypeId2<T>::Defined>::type 
print(const T &t) { 
    qDebug() << t; 
} 

template<class T> 
typename std::enable_if<QMetaTypeId2<T>::Defined>::type 
print(const T &t) { 
    qDebug() << QVariant::fromValue<T>(); 
} 

Da jedoch QMetaTypeId2 nicht dokumentiert ist und nur für interne Sachen, sollte es nicht in Client-Code angezeigt.

+0

Korrigieren Sie mich, wenn ich mich irre: Sie wollen in ** Kompilierzeit wissen ** hat einen Typ in Qt-Meta-System in ** Laufzeit ** registriert registriert ??? Wenn ja - es ist Unsinn – borisbn

+0

Nein. Ich meine "deklariert", d. H. Es ist ein "Q_DECLARE_METATYPE (T)" vorhanden. Entschuldigung, ich werde es korrigieren. – leemes

+0

Die nächste, die ich finden kann, ist 'qMetaTypeId ()', aber es verursacht einen Compiler-Fehler, wenn 'T' nicht registriert ist - nicht so gut. – cmannett85

Antwort

1

Sie sollten einen Wrapper angeben, der Ihnen hilft. Das Beste, was ich denken kann, ist mehrere Definitionen zu haben, auf der Grundlage der version von Qt:

template<typename T> 
struct is_registered 
{ 
    enum 
    { 
     value = 
#if QT_VERSION >= 0x050000 // Qt 5.0.0 
      QMetaTypeId2<T>::Defined 
#elif QT_VERSION >= 0x040000 // Qt 4.0.0 
      QMetaTypeId2<T>::Defined 
#endif 
    }; 
}; 

Das ist nicht ästhetisch ist, aber das ist functionnal, und in Ihrem Code können Sie is_registered<T>::value verwenden, ohne sich um die Version kümmern von Qt. Außerdem habe ich Qt5 im Moment nicht, also kann ich Ihnen nicht sagen, ob QMetaTypeId2<T>::Defined korrekt ist (obwohl ich denke, dass es ist).


Es ist unmöglich, qMetaTypeId<T>() zu verwenden, wenn eine Art registriert wurde, zu überprüfen. Tatsächlich ist der Ausdruck qMetaTypeId<T>() immer gültig, unabhängig vom Typ. Wenn es nicht registriert ist, wird die Körper der Funktion nicht kompilieren (genauer gesagt: in Qt 4 und 5 (im Moment), ruft qMetaTypeId<T>() nur eine andere Funktion, die nicht kompiliert, wenn der Typ nicht registriert ist. So , Sie können SFINAE nicht verwenden, um es zu testen, daher gab der Code leemes in seiner (jetzt gelöschten) Antwort will not work as expected.

Der Code war:

struct _test_is_declared_metatype 
{ 
    template<class T> 
    static auto test(T* t) -> decltype(qMetaTypeId<T>(), std::true_type()); 

    static std::false_type test(...); 
}; 

template<class T> 
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0)) 
{ 
}; 

Warum dies nicht funktionieren wird? Die Absicht war, dass der Aufruf von qMetaTypeId<T>() auf einem nicht registrierten Typ zu einem Kompilierungsfehler führt, "SFINAE würde die erste Funktion ausschließen, wenn der Typ nicht registriert ist". Das Problem hier ist, dass qMetaTypeId<T>() immer ein gültiger Ausdruck ist, also qMetaTypeId<T>(), std::true_type() ist auch, und decltype(qMetaTypeId<T>(), std::true_type()) ist perfekt definiert (mit dem Wert std::true_type).
Dies, weil der Kompilierungsfehler qMetaTypeId<T>() in der Körper der Funktion, nicht in seinem Prototyp stammt (übrigens wird der Code nur kompilieren, wenn die Funktion in decltype deklariert und korrekt aufgerufen wird, dh keine Vorlage Argumente für eine Nicht-Template-Funktion zum Beispiel).
Da diese Überlast von test() spezifischer ist als die variadische, wird sie immer gewählt, daher wird sie immer "zurückgeben", dass der Typ registriert ist; Sie können es in den folgenden Testcode siehe:

// ---------------------------------------------------------- 
// qmetatype.h simplification ------------------------------- 
// ---------------------------------------------------------- 

template<typename T> 
struct metatype 
{ 
enum { defined = 0 }; 
}; 

template<typename T> 
struct metatype2 
{ 
enum { defined = metatype<T>::defined }; 
static inline int id() { return metatype<T>::id(); } 
}; 

template <typename T> 
inline int metatypeId(
    T * /* dummy */ = 0 
) 
{ 
    return metatype2<T>::id(); 
} 

#define register_meta_type(_type_) \ 
template<>       \ 
struct metatype<_type_>   \ 
{         \ 
    enum { defined = 1 };    \ 
    static int id()      \ 
    {         \ 
    /* Run-time registration in Qt */ \ 
    return __COUNTER__;    \ 
    };         \ 
}; 



// ---------------------------------------------------------- 
// ---------------------------------------------------------- 
// ---------------------------------------------------------- 

class TestA {}; 
register_meta_type(TestA) 

class TestB {}; 

class TestC {}; 
register_meta_type(TestC) 

class TestD {}; 


#include <type_traits> 

struct _test_is_declared_metatype 
{ 
/* 
    metatypeId<T>() is always a valid expression. So this overload is 
    always taken 
*/ 
    template<class T> 
    static auto test(T* t) -> decltype(metatypeId<T>(), std::true_type()); 

    static std::false_type test(...); 
}; 

template<class T> 
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0)) 
{ 
}; 

#include <iostream> 
#define PRINT_DEF(_type_) std::cout << #_type_ << " registered ? " << is_declared_metatype<_type_>::value << "\n"; 
int main() 
{ 
std::cout << std::boolalpha; 
PRINT_DEF(TestA); 
PRINT_DEF(TestB); 
PRINT_DEF(TestC); 
PRINT_DEF(TestD); 
} 

Sie möchten vielleicht zu read more about SFINAE. Sie können auch read qmetatype.h here.

+0

Danke. Es ist richtig für beide, das ist nicht der Punkt. Der Punkt ist: Es ist undokumentiert, was bedeutet, dass es sich in der Zukunft wahrscheinlicher ändern kann als dokumentiertes Zeug. Auf der anderen Seite ist zum Beispiel 'qMetaTypeId ()' dokumentiert und bietet auch eine Überprüfung. Es kompiliert nicht, wenn "T" nicht registriert ist. Dies bedeutet, dass ein SFINAE-Trick erforderlich ist, um die "Kompilierbarkeit" in einen booleschen Kompilierzeitwert abzubilden. – leemes

+0

Ich schaute in der Quelle, und für mich kann qMetaTypeId() nicht mit SFINAE verwendet werden. Undokumentierter Code in Qt 4.x wird in diesen Versionen "für immer" vorhanden sein, also denke ich, dass die Verwendung des Präprozessors in Ordnung ist. – Synxis

+0

Warum können Sie 'qMetaTypeId()' nicht mit SFINAE verwenden? Ich bin neu bei SFINAE, also habe ich vielleicht ein paar Details übersehen. – leemes

Verwandte Themen