QMetaEnum and strong typed enum - c++

With plain enums I was able to access Q_ENUMS properties and specific, the character represenation of enums, with following code:
// in .h
class EnumClass : public QObject
{
Q_OBJECT
public:
enum MyEnumType { TypeA, TypeB };
Q_ENUMS(MyEnumType)
private:
MyEnumType m_type;
};
// in .cpp
m_type = TypeA;
...
const QMetaObject &mo = EnumClass::staticMetaObject;
int index = mo.indexOfEnumerator("MyEnumType");
QMetaEnum metaEnum = mo.enumerator(index);
QString enumString = metaEnum.valueToKey(m_type); // contains "TypeA"
If I want to use the c++11 feature for strong typed enums like
enum class MyEnumType { TypeA, TypeB };
accessing the meta information does not work anymore. I guess, that Qt does not recognize it as an enum anymore.
Is there any solution to access the character represenation of an enum while using strong typed enums?

Q_ENUMS is obsolete, and Q_ENUM should be used instead, but the following code works for me with either of them (Qt 5.5, your issue might be caused by an old Qt version; also this question is relevant):
.h:
#include <QObject>
class EnumClass : public QObject
{
Q_OBJECT
public:
enum class MyEnumType { TypeA, TypeB };
EnumClass();
Q_ENUM(MyEnumType)
private:
MyEnumType m_type;
};
.cpp:
#include <QDebug>
#include <QMetaEnum>
#include <QMetaObject>
EnumClass::EnumClass()
{
m_type = MyEnumType::TypeA;
const QMetaObject &mo = EnumClass::staticMetaObject;
int index = mo.indexOfEnumerator("MyEnumType");
QMetaEnum metaEnum = mo.enumerator(index);
// note the explicit cast:
QString enumString = metaEnum.valueToKey(static_cast<int>(m_type));
qDebug() << enumString;
}
main:
int main()
{
EnumClass asd;
return 0;
}
output:
"TypeA"

You can use Q_ENUM macro and template with QMetaEnum:
in.h:
#pragma once
#include <QObject>
#include <QString>
#include <QMetaEnum>
template<typename T>
QString enumToString(T value)
{
int castValue = static_cast<int>(value);
return QMetaEnum::fromType<T>().valueToKey(castValue);
}
class Enum : public QObject
{
Q_OBJECT
public:
enum class Color {
NO_COLOR = 0,
RED,
GREEN,
BLUE,
};
Q_ENUM(Color)
Enum();
Enum(Color color);
QString toString();
private:
Color m_value {Color::NO_COLOR};
};
in.cpp:
#include "in.h"
Enum::Enum()
{
}
Enum::Enum(Color color = Color::NO_COLOR) : m_value(color)
{
}
QString Enum::toString()
{
return enumToString(m_value);
}
main.cpp
#include "in.h"
#include <QDebug>
int main()
{
Enum none;
Enum red(Enum::Color::RED);
Enum green(Enum::Color::GREEN);
Enum blue(Enum::Color::BLUE);
qDebug() << none.toString();
qDebug() << red.toString() << green.toString() << blue.toString();
return 0;
}
output:
"NO_COLOR"
"RED" "GREEN" "BLUE"

When using enum class , cast the type to int in valueToKey():
For a QObject class , we ca directly use QMetaEnum , this direct approach should and actually works, both getting key and value ; tested Qt_5_10
declaration:
class EnumClass : public QObject
{
Q_OBJECT
public:
enum class MyEnumType { TypeA, TypeB };
Q_ENUM(MyEnumType)
...
};
Usage:
QMetaEnum metaEnum = QMetaEnum::fromType<EnumClass::MyEnumType>();
qDebug() << metaEnum.valueToKey(static_cast<int>(EnumClass::MyEnumType::TypeA));
qDebug() << metaEnum.keyToValue("TypeB");
...
result :
"TypeA"
1

Related

"Static polymorphism with Qt signal/slot: What is wrong?"

I'm trying to use static polymorphism instead of dynamics polymorphism with Qt signal/slot mechanism. But I get compile error. What is wrong in my code? What is workaround?
devices.h
#ifndef DEVICES_H
#define DEVICES_H
#include <QtCore>
#include <qdebug.h>
class DeviceController : public QObject
{
Q_OBJECT
public:
explicit DeviceController(QObject *parent = nullptr):QObject(parent){}
virtual ~DeviceController() {}
void doAllDevicesInit(){
emit deviceAInitSignal();
}
signals:
void deviceAInitSignal();
};
template<typename T> class BaseDevice {
public:
void init() {
static_cast<T*>(this)->doInit();
qDebug() << QString("BaseDevice initialized!");
}
};
class DeviceA : public BaseDevice<DeviceA> {
public:
void doInit() {
qDebug() << "DeviceA initialized!";
}
};
#endif // DEVICES_H
main.cpp
#include "devices.h"
int main(int argc, char *argv[])
{
Q_UNUSED(argc);Q_UNUSED(argv);
DeviceA deviceA;
DeviceController deviceController;
QObject::connect(&deviceController,&DeviceController::deviceAInitSignal,
&deviceA, &DeviceA::init);
deviceController.doAllDevicesInit();
return 0;
}
Compile output
Qt5.12.2/5.12.2/gcc_64/include/QtCore/qobjectdefs_impl.h:414:94:
error: invalid static_cast from type ‘QObject*’ to type
‘QtPrivate::FunctionPointer::*)()>::Object*’
{aka ‘BaseDevice*’}
FuncType::template call(static_cast(this_)->function, static_cast(r), a);
Thanks to drescherjm comment, a workaround is as follow
devices.h
...
template<typename T>
class BaseDevice: public QObject {
//Q_OBJECT, Error: Template classes not supported by Q_OBJECT
public:
explicit BaseDevice(QObject *parent = nullptr):QObject(parent){}
virtual ~BaseDevice() {}
void init() {
static_cast<T*>(this)->doInit();
qDebug() << QString("BaseDevice initialized!");
}
};
class DeviceA : public BaseDevice<DeviceA> {
Q_OBJECT
public:
explicit DeviceA(QObject *parent = nullptr):BaseDevice<DeviceA>(parent){}
virtual ~DeviceA() {}
void doInit() {
qDebug() << "DeviceA initialized!";
}
};
#endif // DEVICES_H

Object members not retrieved from a Qt dll

I try to get a QObject-subclassed object from a Qt shared library to a Qt application dynamically.
I have tried to apply a previous answer about this subject : QLibrary - import a class
Here is my common interface tabappinterface.h:
#ifndef TABAPP_H
#define TABAPP_H
#include <QObject>
#include <QWidget>
class TabAppInterface : public QObject
{
Q_OBJECT
public:
virtual ~TabAppInterface()
{
}
QWidget *app;
QString title;
};
#endif // TABAPP_H
My class dll-side mytabapp.h:
#ifndef MYTAB_H
#define MYTAB_H
#include "tabapp_global.h"
#include "tabappinterface.h"
class MYTABSHARED_EXPORT MyTabApp: public TabAppInterface
{
public:
MyTabApp();
virtual ~MyTabApp();
QWidget *app;
QString title;
};
extern "C" MYTABSHARED_EXPORT TabAppInterface *getTabApp();
and its implementation mytabapp.cpp:
#include "mytabapp.h"
MyTabApp::MyTabApp()
{
app = new AppWidget();
title = QStringLiteral("My Tab App");
}
MyTabApp::~MyTabApp()
{
}
TabAppInterface *getTabApp()
{
return new MyTabApp();
}
My app-side implementation:
void ContainerMainWindow::loadLibraries()
{
QLibrary myLib("mytabapp.dll");
if(myLib.isLoaded())
{
qDebug() << "Loaded!";
}
typedef TabAppInterface *(*tabAppGetProt)();
auto tabAppGetter = (tabAppGetProt) myLib.resolve("getTabApp");
if(tabAppGetter)
{
auto *tabApp = tabAppGetter(); // not null
qDebug() << tabApp->title; // print empty string
qDebug() << (QWidget *)(tabApp->app); // SEGFAULT
}
}
As stated in comment in the last lines, the object members are not retrieved although tabApp is not null.
Any idea?
Thanks!
You're accessing the base class variables (fields) title and app, which nobody ever initialized. These variables aren't virtual at all, so those in the derived class just hide those in the base class. As a solution, you can declare them as protected in the base class and encapsulate them in getters and setters.
This way, your base class is like:
class TabAppInterface : public QObject
{
Q_OBJECT
public:
virtual ~TabAppInterface(){}
QWidget *getApp() const { return app; }
void setApp(QWidget *value) { app = value; }
QString getTitle() const { return title; }
void setTitle(const QString &value) { title = value; }
protected:
QWidget *app;
QString title;
};
and the derived class is just like:
class MYTABSHARED_EXPORT MyTabApp: public TabAppInterface
{
public:
MyTabApp();
virtual ~MyTabApp();
};
You can still directly access the variables inside the derived class (i.e. initialize them in constructor) and through the getters/setters methods from outside:
auto *tabApp = tabAppGetter();
qDebug() << tabApp->getTitle();
qDebug() << tabApp->getApp();

Get member of parent struct

I've created an SSCE to better explain this.
In a class, I have a variable, a struct, and a declaration of the struct. Within that struct is a constructor, a variable, a struct and a declaration of that struct. And inside THAT struct is a constructor.
So is goes Mother > Daughter > GDaughter aka class > struct > struct
mother.h
#ifndef MOTHER_H
#define MOTHER_H
#include <QSplitter>
class Mother : public QSplitter
{
Q_OBJECT
public:
Mother(int);
int age;
struct Daughter
{
Daughter();
int height1;
struct GDaughter
{
GDaughter();
};
GDaughter *kate;
};
Daughter *tina;
};
#endif // MOTHER_H
Now let's take a look at the constructors/source. Here lies my issue.
mother.cpp
#include "mother.h"
#include <QDebug>
Mother::Mother(int a)
{
age = a;
tina = new Daughter();
}
Mother::Daughter::Daughter()
{
qDebug() << age; //Not going to work... I get it. Daughter isnt a derived class
height1 = 10;
kate = new GDaughter();
}
Mother::Daughter::GDaughter::GDaughter()
{
qDebug() << height1; //Why not? The GDaughter instance is a member of Daughter!
}
Both qDebug() lines throw is not a type name, static, or enumerator.
The goal is to create "child" structs dynamically. So a parent struct might have 0 child structs, 1 child struct, or even 100 child structs. That is why I am using structs instead of derived classes. This setup looks like it would work except for the problem where "parent" variables can't be accessed.
I will include the other files anyway:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mother.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mom = new Mother(50);
}
MainWindow::~MainWindow()
{
delete ui;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mother.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
Mother *mom;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
If I am misunderstanding how to go about making something like this then let me know.
Thanks for your time.
AFTER ANSWER
In the mother.h I added parent pointers:
struct Daughter
{
Daughter(Mother *p); //Here
int height1;
struct GDaughter
{
GDaughter(Daughter *p); //And here
};
GDaughter *kate;
};
and in mother.cpp I filled in the needed code:
Mother::Mother(int a)
{
age = a;
tina = new Daughter(this); //Here
}
Mother::Daughter::Daughter(Mother *m) //Here
{
qDebug() << m->age; //Here
height1 = 10;
kate = new GDaughter(this); //Here
}
Mother::Daughter::GDaughter::GDaughter(Daughter *d) //Here
{
qDebug() << d->height1; //Here
}
keep pointer(std::weak_ptr if you use smart pointers, for example) to parent class object in nested object.
explicit MainWindow(QWidget *parent = 0);
look at Qt code here, you pass pointer to the parent object in constructor,
if parent is nullptr that means MainWindow has no parent object, Also you can keep
pointer to MainWindow parent object and then static_cast it to exact class
#include <iostream>
class A {
class B{
public:
B(A *p):parent(p) { parent->hello(); }
private:
A *parent;
};
public:
A() { b = new B(this); }
void hello() { std::cout << "hello world" << std::endl; }
private:
B *b;
};

How to access a struct from another another C++ class?

Hello I have a struct in a TreeItem class:
// TreeItem.h
class TreeItem
{
public:
struct TreePair
{
QString sa_key;
QVariant sa_value;
};
//... blabla
}
I would like access that struct TreePair from another class TreeModel , which has class TreeItem already formarded in its header:
// TreeModel.h
class TreeItem;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
//..
}
// TreeModel.cpp
TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent)
: QAbstractItemModel(parent)
{
QVector<TreePair> rootData; // TreePair was not declared in this scope
}
My strcut was not declared in this scope?? In every class it was already automatically set like
#ifndef _TREEITEM_H
#define _TREEITEM_H
#endif
Since TreePair is nested inside TreeItem, it needs to be
QVector<TreeItem::TreePair> rootData;

Can't declare Q_ENUM from an enum defined in another class

This documentation states
If you want to register an enum that is declared in another class, the enum must be fully qualified with the name of the class defining it. In addition, the class defining the enum has to inherit QObject as well as declare the enum using Q_ENUMS().
However I can't make this work, in the following example.
Class A:
#ifndef CLASSA_H
#define CLASSA_H
#include <classb.h>
class ClassA : public QObject
{
Q_OBJECT
Q_ENUMS(ClassB::TestEnum)
public:
explicit ClassA(QObject *parent = 0) : QObject(parent)
{
const QMetaObject *metaObj = this->metaObject();
qDebug() << metaObj->enumeratorCount();
}
};
#endif // CLASSA_H
ClassB:
#ifndef CLASSB_H
#define CLASSB_H
#include <QDebug>
#include <QMetaEnum>
#include <QObject>
class ClassB : public QObject
{
Q_OBJECT
Q_ENUMS(TestEnum)
public:
enum TestEnum { A, B, C };
explicit ClassB(QObject *parent = 0) : QObject(parent)
{
const QMetaObject *metaObj = this->metaObject();
qDebug() << metaObj->enumeratorCount();
}
};
#endif // CLASSB_H
main:
#include <classa.h>
#include <classb.h>
int main()
{
ClassA objectA;
ClassB objectB;
}
Expected output:
1
1
Actual Output:
0
1
Here is a summary of a little research:
Information stated in the documentation about registration of a enum declared in another class looks outdated.
Q_ENUMS(Class::EnumName doesn't create a new enumerator, and useless.
When you declare a new Q_PROPERTY in ClassA you should use full form of the enum ClassB::EnumName.
As soon as EnumName is registered in ClassB it doesn't need to be registered any more.
A property created using a enumerator from another class works correctly.
class ClassA : public QObject
{
public:
Q_OBJECT
Q_PROPERTY(ClassB::TestEnum test READ test)
public:
explicit ClassA(QObject *parent = 0)
{
const QMetaObject *metaObj = this->metaObject();
qDebug() << metaObj->enumeratorCount();
QMetaProperty property = metaObj->property(metaObj->indexOfProperty("test"));
if (property.isEnumType())
{
const QMetaEnum& enumerator = property.enumerator();
qDebug() << enumerator.name();
for (int i = 0 ; i < enumerator.keyCount(); i++)
{
qDebug() << QLatin1String(enumerator.key(i)) << enumerator.value(i);
}
}
}
ClassB::TestEnum test() const
{
return ClassB::A;
}
};
Output:
0
TestEnum
"A" 0
"B" 1
"C" 2