Проверьте, объявлен ли тип как система мета-типов (для SFINAE)

Различить регистр для параметра t типа T используя 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_if и т. Д. Теперь я знаю, что есть QTypeInfo который, я думаю, уже обеспечивает что-то вроде черты типа «объявлено в системе метаданных Qt». Тем не менее, этот класс не документирован, и поэтому не рекомендуется использовать его в долгосрочном и производительном коде, так как он может меняться между версиями Qt.

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

Обратите внимание, что решение все еще должно быть портативный между разными версиями 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 не документирован и только внутренний материал, он не должен появляться в коде клиента.

6

Решение

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

Код был:

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

Вы можете захотеть узнать больше о СФИНАЕ. Также вы можете читать qmetatype.h Вот.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]