4

Let's consider this example:

QVariant v1(1);
QVariant v2("goofy");
QVariantList list;

list << v1 << v2;

for (const auto& var : list) {
   qdebug() << var;

   // nasty part
   if (var.type == QVariant::Int) {
      int value = var.toInt();

      // do something
   } else if (var.type == QVariant::QString) {
      QString value = var.toString();

      // do something
   }
}

The debug function shows the internal storage type of QVariant:

QVariant(int, 1) QVariant(QString, "goofy") 

Is there a way to avoid the ifs and do an explicit cast in order to access the internal type? More specifically, to get the value I would like to be able to write something like this:

auto value = var.ToData();

Edit: Since QVariant can hold a lot of type and you can even add custom types on it, it would be enough restricting the problem only to base types (int, double, bool, string)

Moia
  • 2,216
  • 1
  • 12
  • 34
  • That's the price you pay for using a variant type. `qdebug()` works with `QVariant` because Qt implements `QDataStream& operator<< (QDataStream& s, const QVariant& p)` – Johann Gerell Sep 11 '18 at 07:43
  • That `operator<<``also does much the same "nasty ifs" – Johann Gerell Sep 11 '18 at 07:45
  • 2
    Interesting question, and I think it's not possible because the `auto` is resolved at compile time while the `QVariant` type is set at runtime. If your variant variable was defined at compile time with templates (so not like QVariant does it), it would be doable for sure. – ymoreau Sep 11 '18 at 07:45
  • This is how the data is stored in `QVariant` : https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qvariant.h.html#QVariant::Private – ymoreau Sep 11 '18 at 07:49
  • What would `decltype(var.ToData())` be? – Caleth Sep 12 '18 at 08:33
  • @Caleth it does not exists `toData()`, but as Jaa-c suggested, variant is resolved at Runtime meanwhile decltype is resolved at compile time – Moia Sep 12 '18 at 10:49
  • 1
    @Moia my question was leading you to the conclusion that `toData` *can't* exist, because it doesn't have a unique type. – Caleth Sep 12 '18 at 11:02
  • Please provide some representative examples of exactly what operations you wish to perform on the values - try to be comprehensive, but don't add things you "potentially" might wish to do, but don't have immediate need for. Overgeneralizing may make a solution impossible. – Kuba hasn't forgotten Monica Sep 12 '18 at 12:51
  • `switch(var.type)` and a couple case statements would grab the basic types you are interested in, leaving the default for anything else. – jonspaceharper Oct 15 '18 at 07:42

2 Answers2

6

No, this is not possible. As any other variant, QVariant basically acts as a union. Unless you know the type of the data, you can't get it. If your imaginary code auto value = var.ToData(); should work, the type of value would have to be resolvable at compile time - but then it wouldn't be a variant at all.

The whole point of variant is that it enables you to store multiple types in a single value, all done in runtime. Internally, it stores the type of it's value, but it's a runtime value - so if you don't know the type exactly, there is no other way than to make a long switch.

Jaa-c
  • 5,017
  • 4
  • 34
  • 64
1

It is possible, but at a price - and QVariant doesn't do it. There are two related approaches:

  1. QVariant could dispatch all operators to the type-specific ones at runtime, e.g. adding two QVariants that hold integers could invoke int operator+(int,int). It's not done because it is hard to do it in a sufficiently general way, but if you had a few types in mind - you certainly could make your own type that does it. It could, internally, use QVariant, but I'd be leery of exposing it indiscriminately as a QVariant. It could be convertible to a QVariant, though, so that you could pass the type where a variant is expected, but not have it itself be a variant - i.e. use composition, not inheritance.

  2. QVariant could implement templated type forwarding that would deduce the type based on what operations you perform on it, using SFINAE and template expressions, and replace the operations on QVariant with operations on the contained type, with no runtime overhead. Writing out a sufficiently specific expression would deduce the type, and if it wasn't specific enough, then you'd have to provide type hints - just like in any functional language with type deduction.

When you have a small list of types in mind, either approach could work very well, but with the plethora of types QVariant has to support - including a-priori unknown user types - the general approach to have so many limitations as to be unusable. It's hard to tell without knowing what you do with those values. More could be said about it when given less laconic of a description than //do something.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313