18

Is there a way to use Q_DECLARE_METATYPE() with enum class types? I know old enums work but what about these new, strongly typed, ones? Can't find anything regarding this problem elsewhere. I'm using the latest Qt version available.

Example:

enum Foo
{
    A,
    B,
    C
};

Q_DECLARE_METATYPE(Foo)
QVariant var = Foo::A; // works fine

enum class Bar
{
    X,
    Y,
    Z
};

Q_DECLARE_METATYPE(Bar)
QVariant var = Bar::X; // doesn't compile
Venom
  • 1,107
  • 4
  • 21
  • 46

3 Answers3

25

This is because when you use a plain old enum:

enum Foo { A, B, C };
QVariant var = Foo::A;

the compiler in fact uses the following constructor to build up your var instance:

QVariant(const QVariant& other);

And going further, the other instance is created using the following non-explicit constructor:

QVariant(int val);

This is possible, because old enums can be treated as integral values.

To sum up, this is what the compiler sees and does behind the scene:

int temp = Foo::A;
QVariant var = QVariant(temp);

As you know, enum classes CAN NOT be treated as integral values without an explicit cast. Thus, the compiler cannot implicitly convert your type to int, and invoke a matching constructor (to be precise: the best candidate from all available constructors). That is, there is a predefined set of constructors QVariant offers. You cannot add a new one using Q_DECLARE_METATYPE macro.

To use QVariant with your own type, you should rather use QVariant::fromValue(const T& t) function:

enum class Foo { A, B, C };
QVariant var = QVariant::fromValue(Foo::A);

or alternatively:

enum class Foo { A, B, C };
QVariant var;
var.setValue(Foo::A);
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • Sorry for bothering you, but how do I extract the enum value from the QVariant object? I could use the toInt() method before, but now not so much. – Venom Aug 26 '14 at 20:44
  • 2
    If I try using `QVariant::fromValue()` I get the error "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system". – parsley72 Nov 21 '15 at 05:43
  • Extract enum class value from `QVariant` (assuming it was registered with `Q_ENUM(Foo)`): `Foo foo = var.value();` – phispi Sep 30 '21 at 15:27
7

You can use Q_ENUM which was added in Qt 5.5:

enum class Bar
{
    X,
    Y,
    Z
};
Q_ENUM(Bar)

QVariant var = QVariant::fromValue(Bar::X);
parsley72
  • 8,449
  • 8
  • 65
  • 98
  • 1
    Note that this only works for enum classes defined inside a class deriving from QObject not for globally defined enum classes. – mxmlnkn Aug 02 '19 at 09:12
  • 4
    If you're using a namespace for a globally defined enum you can use `Q_ENUM_NS` which was added in Qt 5.8 – parsley72 Aug 02 '19 at 19:19
0

Tried proposed solution using QVariant::fromValue, but kept getting compiler errors of type:

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

My workround was to explicitly cast the enum values to int when passing them onto a function requiring a QVariant type, in my case when adding a QComboBox item, and then casting it back to my enum class type at value retrieval.

In context of the original question, this would be something akin to:

enum class Foo { A, B, C };

// Elsewhere, adding item to QComboBox.
m_comboBox->addItem(tr("A"), static_cast<int>(Foo::A));

// Value retrieval from QComboBox item, somewhere else.
const auto foo { static_cast<Foo>(m_comboBox->currentData().value<int>()) };
tjansson
  • 324
  • 2
  • 11