检查类型是否声明为元类型系统(对于SFINAE) [英] Check if type is declared as a meta type system (for SFINAE)

查看:165
本文介绍了检查类型是否声明为元类型系统(对于SFINAE)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了区分使用SFINAE的 T 类型的参数 t ,我想知道语句

  QVariant :: fromValue(t); 

和/或

  QVariant :: value< T>(); 

编译。如果一个编译,另一个也是,除非你hack元类型系统。当且仅当 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版本之间变化。



有一个非常简单如果QVariant支持类型 T ,则检查SFINAE专用化中的方式(比使用checker+ enable_if更简单)



请注意,解决方案仍然应该是不同Qt版本之间的可移植(Qt4和Qt5可能使用不同的 QTypeInfo 定义)。但是,我使用C ++ 11,所以我可以访问 std :: enable_if 例如。



非便携式方式是使用 enable_if ()中的 QMetaTypeId2< T> :: Defined 它是一个枚举值,定义为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

解决方案

您应该声明一个包装器,这将有助于您。我最好的想法是有几个定义,基于版本 of 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> ::定义
#endif
}
}

这不是审美,但这是功能性的,在你的代码中,你可以使用 is_registered< T> :: value ,而不必担心Qt的版本。此外,我现在没有Qt5,所以我不能告诉你是否 QMetaTypeId2< T> :: Defined 是正确的(虽然我认为)。






不能使用 qMetaTypeId< T>()以检查类型是否已注册。实际上,表达式 qMetaTypeId< T>()始终有效,无论类型。如果没有注册,则函数的 body 将不编译(更确切地说:在Qt 4和5中(暂时), qMetaTypeId 只调用另一个不编译的函数,如果类型没有注册,那么你不能使用SFINAE来测试它。因此,代码leemes在他的(现在被删除的) =http://ideone.com/jcv6a2 =nofollow>无法按预期工作。



代码是:

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

static std :: false_type test(...);
};
b $ b 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()) $ c> std :: true_type )。

这是因为 qMetaTypeId< T>()的编译错误函数的 body ,而不是它的原型(通过代码只有在 decltype 中的函数被声明并正确调用,例如没有非模板函数的模板参数)。

因此,因为 test()的这个重载比变量一个,它将总是被选择,因此它将总是返回的类型是注册的;您可以在以下测试代码中看到它:

  // -------------- -------------------------------------------- 
// qmetatype.h简化-------------------------------
// --------- -------------------------------------------------

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

#define register_meta_type(_type_)\
template<> \
struct metatype< _type_> \
{\
enum {defined = 1}; \
static int id()\
{\
/ *在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>()始终是一个有效的表达式。所以这个重载是
总是采取
* /
template< class T>
静态自动测试(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<< \\\
;
int main()
{
std :: cout< std :: boolalpha;
PRINT_DEF(TestA);
PRINT_DEF(TestB);
PRINT_DEF(TestC);
PRINT_DEF(testD);
}

您可能需要阅读有关SFINAE的更多信息。此外,您还可以阅读 qmetatype.h 此处。 p>

To make a case distinction for a parameter t of type T using SFINAE, I want to know if the statement

QVariant::fromValue(t);

and / or

QVariant::value<T>();

compiles. If the one compiles, the other one does too, unless you hack the meta type system. They compile if and only if T has been declared using Q_DECLARE_METATYPE(T).

Very simple usage example, where one wants to print the type of a value by simply qDebugging a variant-wrapped equivalent, if and only if supported by the meta type system (I don't need this, but this shows the problem in a minimal example):

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

I know several (yet similar) possibilities to do so, but all of them introduce some helper structs, complicated enable_if etc. Now I know that there is QTypeInfo which, I guess, already provides something like an "is declared in the Qt meta type system" type trait. However, this class is not documented and thus it's not suggested to be used in long term and productive code, as it might change between Qt versions.

Is there a very simple way (simpler than with a "checker" + enable_if) to check in a SFINAE specialization if a type T is supported by QVariant?

Please note that the solution still should be portable between different Qt versions (Qt4 and Qt5 might use a different QTypeInfo definition). However, I use C++11, so I have access to std::enable_if for example.

The "non-portable" way is to use the internal definition of QMetaTypeId2<T>::Defined in an enable_if (it's an enum value defined as either 0 or 1). Thus, a working solution would be:

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

However, since QMetaTypeId2 is not documented and only internal stuff, it should not appear in client code.

解决方案

You should declare a wrapper that will help you. The best I can think is to have several definitions, based on the version of 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
    };
};

That's not aesthetic, but that is functionnal, and in your code you can use is_registered<T>::value without having to worry about the version of Qt. Also, I don't have Qt5 for the moment, so I cannot tell you whether QMetaTypeId2<T>::Defined is correct for it (although I think it is).


It is impossible to use qMetaTypeId<T>() to check if a type was registered. In fact, the expression qMetaTypeId<T>() is always valid, no matter the type. If it is not registered, the body of the function will not compile (to be more precise: in Qt 4 and 5 (for the moment), qMetaTypeId<T>() only calls another function that does not compile if the type is not registered. Thus, you cannot use SFINAE to test it. As a result, the code leemes gave in his (now deleted) answer will not work as expected.

The code was:

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

Why this won't work ? The intention was that because calling qMetaTypeId<T>() on a non-registered type results in a compile error, "SFINAE would exclude the first function when the type is not registered". The problem here is that qMetaTypeId<T>() is always a valid expression, so qMetaTypeId<T>(), std::true_type() is too, and decltype(qMetaTypeId<T>(), std::true_type()) is perfectly defined (with the value std::true_type).
This because the compilation error of qMetaTypeId<T>() stems in the body of the function, not in its prototype (by the way the code will compile only if the function in the decltype is declared and correctly called, ie no template arguments for a non-template function for example).
Thus, because this overload of test() is more specific than the variadic one, it will always be choosed, hence it will always 'return' that the type is registered; you can see it in the following test code:

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

You might want to read more about SFINAE. Also, you can read qmetatype.h here.

这篇关于检查类型是否声明为元类型系统(对于SFINAE)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆