QVariant conversion from UserType to Int



  • I'm trying to take advantage of the new functionality in Qt5.2 allowing conversion from a UserType to an Int. Here's my code:

    @#include <QString>
    #include <QMetaType>
    #include <QDebug>

    class UserTypeClass
    {
    public:
    UserTypeClass(int data=0);
    UserTypeClass( const UserTypeClass& );
    int toInt(bool *ok) const;
    bool operator==( const UserTypeClass& rhs ) const;
    bool operator<( const UserTypeClass& rhs ) const;
    private:
    int m_data;
    };

    Q_DECLARE_METATYPE(UserTypeClass);

    //---------------------------------------------------------------
    UserTypeClass::UserTypeClass(int data) : m_data(data & 0x03)
    {
    qDebug() << "UserTypeClass ctor " << this << ' ' << data << " -> " << m_data;
    }
    //---------------------------------------------------------------
    int UserTypeClass::toInt(bool* ok) const
    {
    if(ok)
    {
    *ok = (m_data >= 0 && m_data < 4); //*ok = true;
    qDebug() << "UserTypeClass::toInt " << this << ' ' << *ok << ' ' << m_data;
    }
    else
    {
    qDebug() << "UserTypeClass::toInt " << this << " not set " << m_data;
    }
    return m_data;
    }
    //---------------------------------------------------------------
    bool UserTypeClass::operator==( const UserTypeClass& rhs ) const
    {
    return m_data == rhs.m_data;
    }
    //---------------------------------------------------------------
    bool UserTypeClass::operator<( const UserTypeClass& rhs ) const
    {
    return m_data < rhs.m_data;
    }
    //---------------------------------------------------------------
    UserTypeClass::UserTypeClass( const UserTypeClass& other )
    : m_data( other.m_data )
    {
    qDebug() << "UserTypeClass copy " << &other << " to " << this << ' ' << m_data;
    }

    int main(int argc, char **argv)
    {
    //QMetaType::registerConverter< UserTypeClass, int > ( &UserTypeClass::toInt );
    int (UserTypeClass::f)(bool) const = &UserTypeClass::toInt;
    QMetaType::registerConverter< UserTypeClass, int > ( f );

    QVariant value;
    value.setValue(UserTypeClass(3));

    qDebug() << "main(): value = " << value.toInt();
    }@

    And here's my output:
    UserTypeClass ctor 0x39fc14 3 -> 3
    UserTypeClass copy 0x39fc14 to 0x758200 3
    UserTypeClass::toInt 0x39fc1c false 6337224
    UserTypeClass::toInt 0x758200 true 3
    main(): value = 3

    I'm using Qt5.2.1 on Windows 7, msvc10. Also tried Qt5.3.1. Haven't tried linux yet...

    My ctor and copy ctor are called as expected, but the toInt() belonging to the object I placed in the QVariant does not seem to get called. How do I know? I cheated - In toInt(), I know m_data should be less than 4, so when I notice it's not, I set ok=false. Only then does the proper toInt() get called (Note 'this' of the second call to toInt() is the same as the copy constructed value). I have no idea where the first call to toInt() originated from.

    What am I doing wrong? I can't rely on this cheat in production, so how can I make sure the correct toInt() will be called in my code?

    (ps, I tried adding a call to QMetaType::registerComparators< UserTypeClass >(); in main, but that didn't help.)

    thanks,

    glenn
    .



  • Debugging the problem shows that "value.toInt()" returns the value of the QVariant value's member variable to store integers and not what it gets from calling UserTypeClass's toInt(bool *) function, though it actually calls this function as the log already proved.

    Sorry, this does not really help you, but at least you know that the problem still exists in Qt 5.3.1 using VS 2013 on WINDOWS 7 64-bit. An Update would not help you.

    I did not use QMetaType::registerConverter before, so the problem interested me. Maybe someone else can have a look to verify the toInt function is properly registered. If well, it looks like a bug.



  • Thanks for looking. I have also confirmed the problem under 5.3.1/linux/g++

    When I call value.toInt(), it generates a call to QVariant::qNumVariantToHelper, which can do 2 attempts to make the conversion:

    @template <typename T>
    inline T qNumVariantToHelper(...)
    {

    ... some code omitted
    
    T ret = 0;
    if ((d.type >= QMetaType::User || t >= QMetaType::User)
        && QMetaType::convert(&val, d.type, &ret, t)) {
        return ret;
    }
    
    if (!handlerManager[d.type]->convert(&d, t, &ret, ok) && ok)
        *ok = false;
    return ret;
    

    }@

    The first attempt uses QMetaType's QMetaTypeConverterRegistry, which can not seem to produce the correct 'this' to find the data value in the qvariant. Fortunately (for debugging, at least), I am able to return false from there and make the second attempt.

    The second attempt uses QVariant's handlerManager and is able to locate the correct data.

    I'm not an expert at this stuff, so I was hoping to get a little more feedback before submitting a bug report.


  • Lifetime Qt Champion

    Hi,

    What about
    @QMetaType::registerConverter< UserTypeClass, int > (&UserTypeClass::toInt);@

    ?



  • That was my first attempt at line 58, then I made my intentions more explicit at lines 59,60. Both give the same result.


  • Lifetime Qt Champion

    Can you try with a more recent version of Qt ? I just tried with exactly your code and it seems to be working fine



  • Please see above. Already tested with 5.3.1. Another commentator has also confirmed with 5.3.1.

    The code 'works' because of the cheat I put in UserTypeClass::toInt(). It sets ok to false when m_data != 3. See line 3 of the log. There, UserTypeClass::toInt() reports a strange value of 'this', false, and 6337224. Where did 6337224 come from? It should be 3. That is the problem.

    Because of the cheat, QVariant::qNumVariantToHelper() is then able to perform a second try at the conversion (again, see above), this time with much improved results.


  • Lifetime Qt Champion

    I might have misunderstood something, in your constructor you ensure that you can't have anything bigger than 3, so in fact when calling toInt you should always have a valid value. Correct ? If so, why implement toInt(bool *ok) and not just toInt() ?

    In the other case, you should simply set ok to true if ok is valid. Otherwise the behavior will be considered as wrong, you must set it even if it's always true.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.