Tuesday, April 11, 2006

Determining if a type is a pointer at compile time

http://doc.trolltech.com/4.1/qlist.html:

"QList is represented as an array of pointers to items. (Exceptionally, if T is a pointer type, a basic type of the size of a pointer, or one of Qt's shared classes, QList stores the item directly in the pointer.)"

So a QList<QRect> is going to be an array of QRect * but QList<QWidget *> will be an array of QWidget * - not QWidget **. But how can we determine whether a type is a pointer at compile time?

Update: This describes the method used in Qt for compilers lacking support for partial template specialisation. If you have partial specialisation, it can be done in a very straightforward way (see blog comments).

Well it seems to be done by abusing our favourite language. QList uses the QTypeInfo class - the interesting bits being:


template <typename T> char QTypeInfoHelper(T*(*)());
void* QTypeInfoHelper(...);

template <typename T>
class QTypeInfo
{
public:
enum {
isPointer = (1 == sizeof(QTypeInfoHelper((T(*)())0))),


int *



Now, consider the case of QTypeInfo<int *>. the last line expands to:

isPointer = (1 == sizeof(QTypeInfoHelper( (int * (*) ()) 0))).

Now I've added some spaces so that it can actually be read. It's saying, pass NULL - casted to a pointer to a function that accepts nothing and returns an int * - to one of the QTypeInfo functions.

But to which 1 of the 2 overloads? Well, the C++ rule for resolving overloaded function calls is to select the function with the most specific matching argument types, assuming no ambiguity. In this case, our function call matches this candidate:

template <typename T> char QTypeInfoHelper(T * (*) ());

because T is int. Now you won't actually find QTypeInfoHelper defined anywhere because all sizeof is interested in is the size of the return value of the function - it doesn't actually execute it. Now, the return type is a char, which is of size 1, therefore isPointer is true.

int



Now we look at a non-pointer type, int. The tricky line expands to:

isPointer = (1 == sizeof(QTypeInfoHelper( (int (*) ()) 0))).

That function pointer argument type accepts nothing and returns an int. It's not going to match the same overload because it's looking for a pointer (the T *):

template <typename T> char QTypeInfoHelper(T * (*) ());

And int is no pointer. Therefore, it can only match the catch-all overload:

void* QTypeInfoHelper(...);

which returns a void* and guess what:

isPointer = (1 == sizeof(void *)).

is false (unless on an 8-bit machine :)). Therefore, int is correctly detected as not a pointer.

Conclusion



QTypeInfo manages to determine whether a type is a pointer without:


  1. actually invoking a function (by merely checking the size of the return type)

  2. ever constructing an element of type T (which might otherwise cause side effects)

5 comments:

Stefan Nikolaus said...

Hi,

some nitpickery: For pointers and basic types separate templates exists. I.e.
int* uses def. at qglobal.h:1366
int uses def. at qglobal.h:1453

Bye,
Stefan

zowers said...

You should look at Boost.TypeTraits library: all is implemented, tested and ported to every compiler

http://www.boost.org/doc/html/boost_typetraits/reference.html#boost_typetraits.is_pointer

Clarence Dang said...

> For pointers and basic types separate
> templates exists. I.e.
> int* uses def. at qglobal.h:1366

Yes, I missed the simpler way if doing it #ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION:


template <typename T>
class QTypeInfo
{
enum {
isPointer = false,

[...]

template <ypename T>
class QTypeInfo<T*>
{
enum {
isPointer = true,


> int uses def. at qglobal.h:1453

s/int/QRect then :)

Vladimir Prus said...

It should be noted that the "two functions" approach is more powerfull then partial template specialization. Same Boost.TypeTraits has traits called is_convertible, which can be used to test if type D is convertible to type B. It can be implemented like this:

char overload(B);
void* overload(...);
D d;
.... sizeof(overload(d));

that's something that can't be done using partial specialization.

Jerome Samson said...

Vampires is not at all like in the movies or books. Sure, I understand. You are young you have the whole world open to you. You can be anything that you choose if you apply yourself and try hard to work toward that goal. But being a Vampire is not what it seems like. It’s a life full of good, and amazing things. We are as human as you are.. It’s not what you are that counts, But how you choose to be. Do you want a life full of interesting things? Do you want to have power and influence over others? To be charming and desirable? To have wealth, health, and longevity? contact the Vampires Lord on his Email: Richvampirekindom@gmail.com