0

Is it possible to inject our own code into QVariant::canConvert?

I'm adding support for converting between our own all-purpose value container and the Qt version (QVariant), based mostly on a suggestion here How to support comparisons for QVariant objects containing a custom type?

I'm pretty sure the answer to this question is "it can't be done", browsing the source shows that the implementation of QVariant::canConvert does not call into the handler, but I thought I would ask anyway out of morbid curiosity.

https://github.com/qtproject/qt/blob/b05d05fd9ce2aeedfaf805a7ed9007a93c902bc9/src/corelib/kernel/qvariant.cpp#L2719

Is there any random place in the QVariant implementation where we can get some sort of hook into this kind of functionality (without recompiling Qt) and if not, is the function on Handler used for anything/why does it exist? Mostly - any suggestions for working around this?

Community
  • 1
  • 1
FrozenKiwi
  • 1,362
  • 13
  • 26

1 Answers1

3

Of course you can use QVariant::canConvert() with your custom data types. The only caveat is you must register the type with the type system. If you do not, you'll get a friendly reminder via a static_assert,

error: static assertion failed: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system

For example, this code works perfectly fine,

struct Foo {
    int a, b;
};

Q_DECLARE_METATYPE(Foo)


int main()
{
        Foo foo {1, 2};
        QVariant variant = QVariant::fromValue(foo);
        qDebug() << variant.canConvert<Foo>();
}

and if you wish to override the default template, all you need is provide your own specialization to do as you wish,

template<>
bool QVariant::canConvert<Foo>() const {
    qDebug() << "Hello from my specialization!";
    return userType() == qMetaTypeId<Foo>();
};

There is no "handlers" because this is all templated. Just be careful - while you can specialize the templates like QVariant::value(), it will not affect the QVariant::toInt() and similar even if you specialize int case. For example,

template<>
int QVariant::value<int>() const
{
    if (userType() == qMetaTypeId<Foo>()) {
        return value<Foo>().a + value<Foo>().b;
    };

    return toInt();
}

....

qDebug() << variant.value<int>() << variant.toInt();

produces,

3 0

user3427419
  • 1,769
  • 11
  • 15
  • Thanks, this answered my question quite well! – FrozenKiwi Feb 04 '16 at 23:12
  • This "works" but I am assuming that once can specialize for example `value()` once otherwise you will get name conflicts during compilation, thus one needs to add all the custom types in the one specialization? Also, I suggest adding `canConvert` for clarity `template<> bool QVariant::canConvert() const { if(userType() == QMetaTypeId()) { return true; } else { return canConvert(qMetaTypeId()); } }` or else it breaks all other `canConvert()` calls – CJCombrink Sep 18 '17 at 13:48