Q_ENUMS and using declarations do not work together - c++

Consider the following class definition:
// exported.hpp
#include <QObject>
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
enum class FOO { BAR };
Q_ENUM(FOO)
};
And the following main file:
// main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include "exported.hpp"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Exported>("Package", 1, 0, "Exported");
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
By doing this, I can easily access the named constants of my enum in QML.
As an example:
// main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import Package 1.0
ApplicationWindow {
Rectangle {
Component.onCompleted: {
console.log(Exported.BAR)
}
}
}
This works as long as the declaration of the enum is enclosed in the class.
As an example, if I change the class definition as shown below, it doesn't work anymore:
// exported.hpp
#include <QObject>
enum class FOO { BAR };
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
using FOO = ::FOO;
Q_ENUM(FOO)
};
Now, Exported.BAR in the QML file is undefined.
The most basic question is: why it doesn't work with using declarations?
Note that it works with forwarded declarations, as an example:
// exported.hpp
#include <QObject>
enum class FOO { BAR };
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
enum class FOO;
Q_ENUM(FOO)
enum class FOO { BAR };
};
This is true to the documentation of Q_ENUM actually (emphasis mine):
This macro registers an enum type with the meta-object system. It must be placed after the enum declaration in a class that has the Q_OBJECT or the Q_GADGET macro.
No mentions of a definition in the whole section.
On the other side, we have this from the standard:
A using-declaration introduces a set of declarations into the declarative region in which the using-declaration appears.
So, I was expecting it to work as well. Anyway, this was maybe a wrong expectation on my side.
That said, any suggestion on how to deal with such an inconvenient?
The only way I can see to work around it in case the enum is defined out of the class is to define another enum within the class and have a one-to-one mapping between them.
It's far from being maintainable and a bit tedious indeed.

This has nothing to do with standard C++, at least not directly. Qt's moc is quite limited in its understanding of C++ syntax rules (and with good reason1). Apparently this way of importing enums into class scopes is outside of it's abilities.
In my code, when I want moc to generate enum <-> string conversions for me, I go with the alias, but in the opposite direction:
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
enum class FOO { BAR };
Q_ENUM(FOO)
};
using FOO = Exported::Foo;
This keeps moc happy and is valid C++. The downside is that you pull up the definition of Exported into every scope you use FOO's definition in, and you can't forward-declare FOO.
1 it was created before libclang was a thing and the implementation cost of a full C++ parser for a few corner cases would be extremely uneconomical.

Related

Proper way to add enum class to metaObject Qt 5.15

I'm tinkering a bit with Qt's meta-object system, and I've come across an issue with adding enum class to a meta-object. I have a struct that contain some variables, one of which is an enum class.
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <QObject>
enum class CarType {
NO_CAR = 0,
SLOW_CAR,
FAST_CAR,
HYPER_CAR
};
struct Player {
Q_GADGET
Q_PROPERTY(Player player READ getPlayer)
Q_PROPERTY(float m_speed READ getSpeed)
Q_PROPERTY(CarType m_carType READ getCarType)
Player getPlayer() { return *this;}
float getSpeed() {return m_speed;}
CarType getCarType() { return m_carType;}
public:
CarType m_carType;
float m_speed;
}; Q_DECLARE_METATYPE(Player)
#endif // EXAMPLE_H
I declare this struct as a Q_META_TYPE, in order to access it with the meta-object system. This allows me to access the struct's properties. Here is my main.cpp:
#include <QCoreApplication>
#include <iostream>
#include <QMetaObject>
#include "example.h"
#include <QDebug>
#include <QMetaProperty>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaType<Player>();
const QMetaObject* metaObject = &Player::staticMetaObject;
qInfo()<< "Number of enums is: " << metaObject->enumeratorCount();
return a.exec();
}
I expect that enumeratorCount() would be 1, however I get 0. I've searched about this issue, and most examples I could find have the struct inside a class, and the Q_ENUM declaration right after it, like this:
enum class CarType {
NO_CAR = 0,
SLOW_CAR,
FAST_CAR,
HYPER_CAR
}; Q_ENUM(CarType)
This, however, results in an error. What's the proper way to register this enum in the meta-object system?
Thanks for your time.
EDIT
The errors that I get that are relevant are:
/home/raphael/SO_Example/example.h:10: error: ‘friend’ used outside of class
/home/raphael/SO_Example/example.h:10: error: ‘staticMetaObject’ was not declared in this scope; did you mean ‘qt_getQtMetaObject’?
If you don't want to add your enum to a class with Q_OBJECT or Q_GADGET, then the other way to do it is to put your enum inside a Q_NAMESPACE. You can't actually use Q_ENUM with Q_NAMESPACE, but you can use Q_ENUM_NS instead. I haven't tested this myself, but it should work:
namespace MyNamespace
{
Q_NAMESPACE
enum class MyEnum {
Foo,
Bar
};
Q_ENUM_NS(MyEnum)
}

Issues while trying to define a class within a namespace (allocation of incomplete type)

I am trying to create a class in Qt within the Ui namespace.
In my name space I already have 2 classes made with Qt Designer, and using it as reference did not help me solve my problem.
In myclass.h I put
namespace Ui {class MyClass};
class MyClass : public QObject
{Q_OBJECT
private:
Ui::MyClass* ui;
//body
}
And in myclass.cpp I put
MyClass::MyClass(QObject *parent) :QObject(parent),ui (new Ui::MyClass)
//Here I have : allocation of incomplete type 'Ui::MyClass'
{
ui->setupUi(this);
//Here I have : member access into incomplete type Ui::MyClass'
}
I saw way of defining class in a namespace but I did not find a way to solve my error here.
This doesn't look like a Qt issue. It's just C++. To define a class in a namespace, just put the declaration within the namespace:
namespace Ui
{
class MyClass : public QObject
{
Q_OBJECT
private:
MyClass* ui;
}; // MyClass
} // Ui

difference of member when accessing by this pointer and member pointer of itself (simple QT example question)

I've been following a simple QT tutorial and came up with a weird noob question.
https://www.youtube.com/watch?v=F56fSKoNCtk&list=PLS1QulWo1RIZiBcTr5urECberTITj7gjA&index=5
this is the tutorial.
Here's the my_window.h
#ifndef MY_WINDOW_H
#define MY_WINDOW_H
#include <QMainWindow>
namespace Ui {
class MyWindow;
}
class MyWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MyWindow(QWidget *parent = nullptr);
~MyWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::MyWindow *ui;
};
#endif // MY_WINDOW_H
and my_window.cpp.
#include "my_window.h"
#include "ui_mywindow.h"
MyWindow::MyWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MyWindow)
{
ui->setupUi(this);
connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),
ui->progressBar,SLOT(setValue(int)));
}
MyWindow::~MyWindow()
{
delete ui;
}
void MyWindow::on_pushButton_clicked()
{
ui->label->setText("Clicked!");
}
void MyWindow::on_pushButton_2_clicked()
{
ui->label->setText("Why...?");
}
In my_window.cpp, the definition of the constructor,
it does the tasks via the member pointer, 'ui' which is a pointer of 'MyWindow type' instance.
I thought 'this' pointer is also a 'MyWindow' class type pointer so i thought i can access the horizontalSlider and progressBar with this pointer too.
But i couldn't.
what makes the difference between two of them?
Your class MyWindow that you define in my_window.h and implement in my_window.cpp is ::MyWindow. Then you have ::Ui::MyWindow which is a totally separate class, and is defined and implemented in auto-generated files by Qt and the Qt tools.
You can have many symbols with the same name, as long as they are defined in different scopes. These symbols can even be different types, like one could be a class, one could be a variable, one could be a function, etc.

Why is the rationale to forward declare and define in the same header?

I'm learning QT and when I create a project this is the header of the widget class:
#ifndef NOTEPAD_H
#define NOTEPAD_H
#include <QMainWindow>
namespace Ui {
class Notepad;
}
class Notepad : public QMainWindow
{
Q_OBJECT
public:
explicit Notepad(QWidget *parent = 0);
~Notepad();
private:
Ui::Notepad *ui;
};
#endif // NOTEPAD_H
Why not just define the class inside the namespace scope? Is this a best practice stuff?
This code is forward declaring Ui::Notepad, then defining ::Notepad. They are not the same type, despite having the same name (which is a silly decision in itself).

ISO C++ forbids declaration of 'auto_ptr' with no type

I'm trying to write a small application and have come across a compile time error using auto_ptr.
I originally tired creating a smart pointer with class I created but the same error occurs if I try and create a smart pointer of type int so there must be something else I'm doing wrong. I was following the example given here..
I've got a feeling the answer to this will result in me slapping myself.
I declare the smart pointer at the bottom of this file.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <memory.h>
#include <QMainWindow>
#include "dose_calac.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
/*
Some QT stuff here, removed for clarity / size...
*/
private:
Ui::MainWindow *ui;
/*
Object for storage of data and calculation of DOSE index score.
*/
std::auto_ptr<int> pdoseIn(new int); // A simple set case, but sill produces an error!?!
std::auto_ptr<DOSE_Calac> pdoseIn(new DOSE_Calac); // Original code, error found here at first.
};
#endif // MAINWINDOW_H
and this is my class, dose_calac.h.
#ifndef DOSE_CALAC_H
#define DOSE_CALAC_H
class DOSE_Calac
{
public:
// constructor
DOSE_Calac();
// set and get functions live here, removed for clarity / size.
// function for caulating DOSE indexpoints
int CalcDOSEPoints();
private:
unsigned int dyspnoeaScale;
unsigned int fev1;
bool smoker;
unsigned int anualExacerbations;
unsigned int doseIndexPoints;
};
#endif // DOSE_CALAC_H
Any help or suggestion gratefully received.
Your error is caused by including an incorrect header. Instead of
#include <memory.h>
you should write
#include <memory>
Also, there is more severe mistake in your class definition, because you cannot initialize class member in this way:
std::auto_ptr<int> pdoseIn(new int);
You have to, separately, declare it and initialize in the constructor:
std::auto_ptr<int> pdoseIn;
MainWindow()
: pdoseIn(new int)
{}
You can not initalize the class member variables like that, you need to define it in the class declaration by doing std::auto_ptr<int> a; and initialize it in ctor using a(new int).
You can't initialize data members inside the class declaration like this:
class MainWindow
{
std::auto_ptr<int> pdoseIn(new int);
};
You need to declare the member like this, and initialize the data member in the constructor:
class MainWindow
{
std::auto_ptr<int> pdoseIn;
MainWindow ()
: pdoseIn(new int)
{
}
};