Direct comparing of two QVariant variables



  • Is it possible to directly compare two QVariant variables for more or less without writing a chain of ifs and explicit casting of QVariant value to the stored type? I mean just base types like ints, strings, etc which allow this type of comparison


  • Moderators



  • How can this help me find out which value is greater and which is less?


  • Moderators

    If you like to use QVariant for sorting or similar it has some stumblestones. The defined operators are the ones you may savely supply.

    Depending what you like to do you may write your own operator overlays. BUT it bears quite a bit of danger! Because you might have to compare non-compatible values and what return value do you want to supply?

    However, when you know the type you may do
    @
    if ( var1.toInt() > var2.toInt() )
    {
    ...
    }
    @



  • This is exactly what I was all out trying to avoid. Yes, I know beforehand which types are stored in QVariant and that they are comparable this way. But writing a bunch of ifs as you suggest and as I actually do seems a bit cumbersome considering that QVariant knows at runtime which type is stored inside it (e.g. QVariant::type() function). You should see the point of frustration


  • Moderators

    You could write an overlay like:
    @
    bool operator< ( QVariant varl, QVariant varr )
    {
    if ( varl.type () == varr.type() )
    {
    return varl.to[use type] < varr.to[use type];
    }
    return [but what are you using here?];
    }
    @

    However again, if you are writing such a method you may be up to a lot of trouble. In future it might hard to find you error, when your program behaves weird.



  • You could write an operator<() for QVariant. It is not defined by default, I assume because it does not make sense for a lot of situations. What should be the result if you try to compare an integer and a QStringList, both of which can be stored in a QVariant?

    Anyway, if in your application, you deal with lots of QVariants of the same type, then it might make sense to define it anyway. The implementation may indeed need to have a lot of case statements, but you'd only need to do this once.



  • Ok, this seems a better idea. In case types mismatch the answer is obvious - assert(0) should be called



  • [quote author="Andre" date="1326626635"]Anyway, if in your application, you deal with lots of QVariants of the same type, then it might make sense to define it anyway. The implementation may indeed need to have a lot of case statements, but you'd only need to do this once[/quote]

    I have already done this and I don't like it, thus this question appearing



  • Assert is nice and all, but it will be compiled out in release mode. I think that for something like this, you might want to throw an exception. At least, you need to have defined the behaviour for the case.



  • [quote author="Andre" date="1326626635"]What should be the result if you try to compare an integer and a QStringList, both of which can be stored in a QVariant?[/quote]

    I can just as well (or as bad as you would say) compare an integer and a QStringList without the "help" of QVariant with the same result. This "type safety" seems a bit forced for a language like c/c++

    [quote author="Andre" date="1326626947"]Assert is nice and all, but it will be compiled out in release mode. I think that for something like this, you might want to throw an exception. At least, you need to have defined the behaviour for the case. [/quote]

    Yes, you're right, this is what I was trying to say. Actually, there is no problem if you know what you are doing



  • [quote author="deisik" date="1326626940"][quote author="Andre" date="1326626635"]Anyway, if in your application, you deal with lots of QVariants of the same type, then it might make sense to define it anyway. The implementation may indeed need to have a lot of case statements, but you'd only need to do this once[/quote]

    I have already done this and I don't like it, thus this question appearing

    [/quote]

    I guess you'd need to talk to somebody who really masters the black arts of template metaprogramming. Perhaps you could achieve this using that way, but I really don't know for sure. I think it will be very tricky, but I very well may be overlooking an obvious solution.



  • [quote author="koahnig" date="1326626572"]
    @
    return varl.to[use type] < varr.to[use type];
    @
    [/quote]

    Is it possible to make automatic conversion, so as to avoid multiple returns? Otherwise, it is just another way of writing

    [quote author="koahnig" date="1326625090"]

    @
    if ( var1.toInt() > var2.toInt() )
    {
    ...
    }
    @
    [/quote]



  • [quote author="Andre" date="1326627612"]
    I guess you'd need to talk to somebody who really masters the black arts of template metaprogramming. Perhaps you could achieve this using that way, but I really don't know for sure[/quote]

    I think, this won't help since templates are resolved at compile-time which is surely not the case here


  • Moderators

    [quote author="deisik" date="1326627966"][quote author="koahnig" date="1326626572"]
    @
    return varl.to[use type] < varr.to[use type];
    @
    [/quote]

    Is it possible to make automatic conversion, so as to avoid multiple returns? Otherwise, it is just another way of writing

    [quote author="koahnig" date="1326625090"]

    @
    if ( var1.toInt() > var2.toInt() )
    {
    ...
    }
    @
    [/quote]

    [/quote]

    At least you have to do it only once.

    [Edit] -As Andre suggested may be if you involve the "black arts" of template meta programming. Right away nothing pops up immediately.-
    Since we cross-posted



  • [quote author="koahnig" date="1326629012"]
    At least you have to do it only once[/quote]

    Already done so and not satisfied. There should be a cleaner solution

    [quote author="koahnig" date="1326629012"]Right away nothing pops up immediately[/quote]

    Stuck too



  • There cannot be a clean solution for an in principle unsolvable problem. QVariant can hold types which are plain not comparable. For int an QStringList one can imagine something fancy, but comparing a QVariantMap against a QImage is ... weird.

    So, you will have to constraint your comparison - this is were your home brown solution enters the game.

    For using a template based solution, something like this comes into mind:

    @
    template<typename T>
    bool qVariantLessThan(const QVariant &l, const QVariant &r)
    {
    QVariant l2 = qvariant_cast<T>(l);
    QVariant r2 = qvariant_cast<T>(r);

    if(l.type() != l2.type() || l != l2) {
        qFatal("QVariant lessThan: l is not of correct type!");
        return false;
    }
    
    if(r.type() != r2.type() || r != r2) {
        qFatal("QVariant lessThan: r is not of correct type!");
        return false;
    }
    
    return l.value<T>() < r.value<T>();
    

    }

    QVariant s1("x1");
    QVariant s2("x2");
    qDebug() << qVariantLessThan<QString>(s1, s2);
    // true

    QVariant i1(1);
    QVariant i2(-1);
    qDebug() << qVariantLessThan<int>(i1, i2);
    // false

    QVariant d1(1.25);
    QVariant d2(1.24);
    qDebug() << qVariantLessThan<double>(d1, d2);
    // false

    QVariant f1(1.25f);
    QVariant f2(1.26f);
    qDebug() << qVariantLessThan<float>(f1, f2);
    // true

    qDebug() << qVariantLessThan<float>(d1, f2);
    // error

    qDebug() << qVariantLessThan<float>(f1, f2);
    // error
    @

    I don't know whether it's possible to overload operator<() at all. Even if it was possible a syntax like

    @
    bool lessThan = x1 < <int> x2;
    @

    would be weird at best.



  • [quote author="Volker" date="1326642057"]There cannot be a clean solution for an in principle unsolvable problem. QVariant can hold types which are plain not comparable. For int an QStringList one can imagine something fancy, but comparing a QVariantMap against a QImage is ... weird[/quote]

    If QVariant knows (and it does) about the type of the value it stores, why can't it return this value with QVariant::value() without type specification just as it returns with value<int>(), value<float>(), etc ad nauseum?

    [quote author="Volker" date="1326642057"]For using a template based solution, something like this comes into mind[/quote]

    This is not a real solution. As said before, it's better to make type casting directly in the if statement for comparison. In both cases you have to know the right type to which to convert at compile-time or to cover all possible (allowed) types. This is a real nuisance and in tight loops just a waste of cycles



  • [quote author="deisik" date="1326646561"]If QVariant knows (and it does) about the type of the value it stores, why can't it return this value with QVariant::value() without type specification just as it returns with value<int>(), value<float>(), etc ad nauseum?[/quote]
    It can't do that, because C++ won't let you. You cannot overload a method by only changing its return type. The only way to do it, is the way it has been done: as a templated method. How on earth is the compiler supposed to know what the return type of QVariant::value() is?

    [quote author="deisik" date="1326646561"]This is not a real solution. As said before, it's better to make type casting directly in the if statement for comparison. In both cases you have to know the right type to which to convert at compile-time or to cover all possible (allowed) types. This is a real nuisance and in tight loops just a waste of cycles[/quote]

    I am wondering if QVariant really is the type for you. If you know you are storing a certain type, why not use that type directly then? QVariant has its uses, but in the end, it still is little more than a glorified void* after all.



  • [quote author="Andre" date="1326656285"]It can't do that, because C++ won't let you. You cannot overload a method by only changing its return type. The only way to do it, is the way it has been done: as a templated method. How on earth is the compiler supposed to know what the return type of QVariant::value() is?[/quote]

    Why should the compiler know anything if the return type can just be a void* pointer of that very QVariant::value() without explicit type cast (or call it valueVoid() for that matter)? I don't mean overloading and again, I see no problem here - qt knows the right type, so does the programmer

    [quote author="Andre" date="1326656285"]I am wondering if QVariant really is the type for you. If you know you are storing a certain type, why not use that type directly then? QVariant has its uses, but in the end, it still is little more than a glorified void* after all. [/quote]

    I have the same thoughts but I don't like the idea of doing some very low level (read nasty) things in qt/c++. Look, in c/c++ you can easily reinterpret some portion of memory as you like on the fly and you cheerfully hope that the exact way you do this can just as easy be determined by some other bytes in memory you point to, but nope, you can't do this



  • [quote author="deisik" date="1326660493"]
    Why should the compiler know anything if the return type can just be a void* pointer of that very QVariant::value() (or call it valueVoid() for thet matter) without explicit type cast? Again, I see no problem here - qt knows the right type, so does the programmer
    [/quote]

    The signature of method value is

    @
    template <typename T>
    T QVariant::value();
    @

    T is a type and must be know at compile time. The pure reason for the existence of QVariant is that its type is not know at compile time, but only during runtime. So we have an unresolvable problem here. This is a limitation of C++, as Andre already stated, so Qt cannot be of any help here.



  • [quote author="Volker" date="1326661096"]T is a type and must be know at compile time[/quote]

    I agree, but this is not the matter in question. Make another method, call it whatever, let it return void* pointer, and compiler is out of question



  • [quote author="deisik" date="1326660493"]Why should the compiler know anything if the return type can just be a void* pointer of that very QVariant::value() without explicit type cast (or call it valueVoid() for that matter)? I don't mean overloading and again, I see no problem here - qt knows the right type, so does the programmer[/quote]
    But that would not help you solve your problem, I think? I mean, comparing two void pointers would compare their addresses, not their contents. So, that would force to to start casting again, and then you're back to where you started.



  • And what do you want to do with the void pointer? That is as opaque as the QVariant itself...



  • [quote author="Andre" date="1326661453"]But that would not help you solve your problem, I think? I mean, comparing two void pointers would compare their addresses, not their contents. So, that would force to to start casting again, and then you're back to where you started. [/quote]

    Yes, there is a problem here but it deserves and requires thorough thinking over. At least we are no longer bound to QVariant without losing its advantages



  • [quote author="Volker" date="1326661563"]And what do you want to do with the void pointer? That is as opaque as the QVariant itself...[/quote]

    I can ask you another question, how QVariant itself stores its value data type?



  • [quote author="deisik" date="1326661825"]
    Yes, there is a problem here but it deserves and requires thorough thinking over. At least we are no longer bound to QVariant without losing its advantages[/quote]

    When passing a void pointer to another method (operators are methods too) you even loos the information that it actually is a QVariant. We're all looking forward to see your implementation outline of a lessThan implementation taking to void pointers, though. I'm pretty sure neither Andre, nor me or anyone else we both know a decent solution, be we're all eager to learn something new (as everyone doing software development should do).



  • A void* caries less information than QVariant. I don't see how that would bring you any closer to a solution.



  • [quote author="deisik" date="1326662003"][quote author="Volker" date="1326661563"]And what do you want to do with the void pointer? That is as opaque as the QVariant itself...[/quote]

    I can ask you another question, how QVariant itself stores its value data type?

    [/quote]

    http://qt.gitorious.org/qt/qt/blobs/4.8/src/corelib/kernel/qvariant.h#line358



  • [quote author="Andre" date="1326662334"]A void* caries less information than QVariant. I don't see how that would bring you any closer to a solution. [/quote]

    This information about data type is known beside QVariant (read saved somewhere else) - this is the whole point of my question. How to use it for comparison and whether it is possible in c++ at all is another question



  • [quote author="Volker" date="1326662484"]http://qt.gitorious.org/qt/qt/blobs/4.8/src/corelib/kernel/qvariant.h#line358[/quote]

    I expected to see something like that



  • [quote author="deisik" date="1326662787"][quote author="Andre" date="1326662334"]A void* caries less information than QVariant. I don't see how that would bring you any closer to a solution. [/quote]

    This information about data type is known beside QVariant (read saved somewhere else) - this is the whole point of my question. How to use it for comparison and whether it is possible in c++ at all is another question[/quote]

    You put void pointers into the game, so it's your turn to show how it eases solving the problem, not ours. Our opinion is known (void pointers make matters worse). The other constraints, set by C++, have already been pointed out.

    [quote author="deisik" date="1326662903"][quote author="Volker" date="1326662484"]http://qt.gitorious.org/qt/qt/blobs/4.8/src/corelib/kernel/qvariant.h#line358[/quote]

    I expected to see something like that

    [/quote]

    Reading source code is the best way of understanding things. What else would you have hoped to see instead of your expectations?



  • [quote author="Volker" date="1326663446"]
    You put void pointers into the game, so it's your turn to show how it eases solving the problem, not ours. Our opinion is known (void pointers make matters worse). The other constraints, set by C++, have already been pointed out.[/quote]

    Ok, I will make a try

    [quote author="Volker" date="1326663446"]Reading source code is the best way of understanding things. What else would you have hoped to see instead of your expectations?[/quote]

    To hope for something out of nothing is the most popular form of hope. Black magic indeed



  • [quote author="Volker" date="1326663446"]
    You put void pointers into the game, so it's your turn to show how it eases solving the problem, not ours[/quote]

    My try as promised. I am interested in comparing ints, floats and strings and as I said earlier data type for each value is known. This means that we know beforehand the length the byte array representing this or that value takes in memory. So the algorithm for comparing two values is to regard them as, say, 64 bits integers, filling the higher missing bits of the shorter values (copy of) with zeroes (to clean up garbage)

    This technique should work even for real numbers. In fact, interpreting the bit-pattern of a floating-point number as integer is one of the methods for comparing floats and doubles (as a side note, you cannot correctly compare two floats by comparing them "out of the box" as you suggested in the code snippet, but I think you know this yourself)

    There still remains a minor problem with negative numbers but as it happens I am not much interested in them (in my case there are none), furthermore I guess this can be fixed somehow

    Now you give me a void pointer to a QVariant value and I show you the code



  • [quote]I am interested in comparing ints, floats and strings and as I said earlier data type for each value is known.[/quote]

    Do you mean that the variants you want to compare are only of these three types? An arbitrary string or a string representing a number as well? (In the former case, how do you compare it f.i. against a number?)



  • [quote author="peppe" date="1326672292"]Do you mean that the variants you want to compare are only of these three types? An arbitrary string or a string representing a number as well? (In the former case, how do you compare it f.i. against a number?)[/quote]

    There can be no comparison between defferent types (imagine a table where you compare only between values of the same column). And yes, columns of this imaginary table are almost all kinds of unsigned base types but they are all interpreted as quint64 integer for saving in a binary file (well, this is a waste of space but it saves time when retrieving them and when we come to file sizes over a few GBs it makes no difference - you just save the value in place of otherwise a 64 bit pointer)

    [quote author="peppe" date="1326672292"]An arbitrary string or a string representing a number as well? (In the former case, how do you compare it f.i. against a number?)[/quote]

    Yes, arbitrary text strings. In case there is a "number" inside it is valid to consider it as text. So "75" would actually be 55 53 as a byte array

    There are also QDateTimes but they are converted to uints with QDateTime::toTime_t(). In fact, all these types pass in QVariant as raw bytes (by QByteArray) and converted to real int, floats, strings and dates only for representation through QAbstractItemModel and building indexes (where comparison comes into play). It is a matter of interpretation



  • And it seems now that I can really make comparisons without QVariant entirely through byte arrays



  • I am still wondering how this byte comparison is going to help you achieve the goal you stated earlier:

    [quote author="deisik" date="1326624029"]How can this help me find out which value is greater and which is less?[/quote]

    How, you think, is a byte-level comparision going to help you compare two QStrings stored in that QVariant union? I mean: you do realize that the actual character array is not stored within the QVariant, right?



  • [quote author="Andre" date="1326703007"]How, you think, is a byte-level comparision going to help you compare two QStrings stored in that QVariant union? I mean: you do realize that the actual character array is not stored within the QVariant, right?
    [/quote]

    Right, it is stored somewhere in memory, so rises the question of a void* pointer to this character array. You seem to be missing the whole point of what I say



  • Then, I guess we just don't understand each other. I wish you all the luck with your attempts to efficiently compare QVariants, but I don't think I can contribute to this discussion anymore. Please let us know if you find an efficient solution, I would be interested to see it.


Log in to reply
 

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