2013-04-16 5 views
6

Чтобы сделать тематическое различие для параметра t типа T с помощью SFINAE, я хочу знать, если заявлениеПроверьте тип объявлен как мета-системы типа (для SFINAE)

QVariant::fromValue(t); 

и/или

QVariant::value<T>(); 

компилируется. Если один компилируется, другой делает тоже, если вы не взломаете систему мета-типа. Они компилируются тогда и только тогда, когда T был объявлен с использованием Q_DECLARE_METATYPE(T).

Очень простой пример использования, когда вы хотите напечатать тип значения, просто qDebugging эквивалентный вариант, если и только если поддерживается системой мета-типа (мне это не нужно, но это показывает проблема в минимальном примере):

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>(); 
} 

Я знаю несколько (пока подобные) возможности сделать это, но все из них ввести некоторые вспомогательные структуры, сложные enable_ и т.д. Теперь я знаю, что есть QTypeInfo, который, я думаю, , уже предоставляет нечто вроде функции «объявляется в типе системы типа Qt». Однако этот класс не документирован, и поэтому не рекомендуется использовать его в долгосрочном и продуктивном коде, поскольку он может меняться между версиями Qt.

Есть ли очень простой способ (проще, чем с «checker» + enable_if) проверять специализацию SFINAE, если QVariant поддерживает тип T?

Обратите внимание, что решение по-прежнему должно быть портативным между различными версиями Qt (Qt4 и Qt5 могут использовать другое определение QTypeInfo). Тем не менее, я использую C++ 11, поэтому у меня есть доступ к std::enable_if, например.

«Непортативный» способ заключается в использовании внутреннего определения QMetaTypeId2<T>::Defined в enable_if (это значение перечисления, определяемое как 0 или 1). Таким образом, рабочий раствор будет:

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>(); 
} 

Однако, поскольку QMetaTypeId2 не документирован и только внутренний материала, он не должен появляться в коде клиента.

+0

Исправьте меня, если я ошибаюсь: вы хотите знать в ** время компиляции ** сделал ли какой-либо тип зарегистрированным в Qt-мета-системе в ** время выполнения ** ??? Если да - это абсурд – borisbn

+0

Нет. Я имею в виду «объявленный», то есть присутствует «Q_DECLARE_METATYPE (T)». Извините, я исправлю это. – leemes

+0

Ближайшим, что я могу найти, является 'qMetaTypeId ()', но он вызывает ошибку компилятора, если 'T' незарегистрирован - не так хорошо. – cmannett85

ответ

1

Вы должны объявить обертку, которая поможет вам. Лучшее, что я могу думать, чтобы иметь несколько определений, на основе version из 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 
    }; 
}; 

Это не эстетично, но это Функциональное и в коде вы можете использовать is_registered<T>::value без необходимости беспокоиться о версии Qt. Кроме того, на данный момент у меня нет Qt5, поэтому я не могу сказать, правильно ли это QMetaTypeId2<T>::Defined (хотя я думаю, что это так).


невозможно использовать qMetaTypeId<T>(), чтобы проверить, если тип был зарегистрирован. Фактически, выражение qMetaTypeId<T>() всегда действует независимо от типа. Если он не зарегистрирован, тело функции не будет компилироваться (точнее: в Qt 4 и 5 (на данный момент) qMetaTypeId<T>() вызывает только другую функцию, которая не компилируется, если тип не зарегистрирован. , вы не можете использовать SFINAE для его проверки. В результате код leemes дал в своем (теперь удаленном) ответе will not work as expected.

Код был:

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)) 
{ 
}; 

Почему это не будет работать? Цель состояла в том, что, поскольку вызов qMetaTypeId<T>() по незарегистрированному типу приводит к ошибке компиляции, «SFINAE исключит первую функцию, если тип не зарегистрирован». Проблема здесь в том, что qMetaTypeId<T>() всегда является допустимым выражением, поэтому qMetaTypeId<T>(), std::true_type() тоже, и decltype(qMetaTypeId<T>(), std::true_type()) отлично определен (со значением std::true_type).
Это связано с ошибкой компиляции qMetaTypeId<T>() в корпусе функции, а не в ее прототипе (к примеру, код будет компилироваться только в том случае, если функция в decltype объявлена ​​и правильно вызывается, то есть без аргументов шаблона для например, функция без шаблона).
Таким образом, поскольку эта перегрузка test() более конкретна, чем вариационная, она всегда будет выбрана, поэтому она всегда будет «возвращать», что тип зарегистрирован; Вы можете увидеть это в следующем коде тест:

// ---------------------------------------------------------- 
// 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); 
} 

Вы можете read more about SFINAE. Кроме того, вы можете read qmetatype.h here.

+0

Спасибо. Это правильно для обоих, это не главное. Дело в том, что он недокументирован, а это значит, что он может измениться в будущем скорее, чем документированный материал. С другой стороны, например, 'qMetaTypeId ()' документируется и также обеспечивает проверку. Он не скомпилируется, если 'T' не зарегистрирован. Это означает, что для передачи «компиляции» в значение логического времени компиляции требуется трюк SFINAE. – leemes

+0

Я посмотрел в источнике, и для меня qMetaTypeId() не может использоваться с SFINAE. Кроме того, недокументированный код в Qt 4.x будет присутствовать в этих версиях «навсегда», поэтому использование препроцессора в порядке, я думаю. – Synxis

+0

Почему, по вашему мнению, не может использоваться 'qMetaTypeId()' с SFINAE? Я новичок в SFINAE, поэтому, возможно, я забыл некоторые подробности. – leemes

 Смежные вопросы

  • Нет связанных вопросов^_^