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)
}
Related
I have an error in my code. All the forums says "in this situation, use Forward Declaration". I tried this and it don't work.
the error is C2079 Credit::mainMenu use a class of MainMenu not declared (translated from french)
I modified the classe to be specific to the question.
main.cpp
#include <SFML/Graphics.hpp>
#include "Menu.h"
#include "credit.h"
#include "mainmenu.h"
MainMenu *mainMenu = new MainMenu();//works here
int main(){
return 0;
}
credit.h
#ifndef DEF_CREDIT
#define DEF_CREDIT
#include <SFML/Graphics.hpp>
#include "Menu.h"
class MainMenu;
class Credit : public Menu
{
private:
MainMenu mainMenu = MainMenu();//DON'T WORK HERE
};
#endif
mainmenu.h
#ifndef DEF_MAINMENU
#define DEF_MAINMENU
#include <SFML/Graphics.hpp>
#include "Menu.h"
#include "credit.h"
class MainMenu :public Menu
{
private:
Credit credit = Credit();//work
};
#endif
Fist i load main.cpp
after the credit.h (there is a credit.hpp but i don't use the variable now)
after the mainmenu.h (there is also a mainmenu.hpp, i don't use variable now)
tip: the Menu class isn't including any of those class
I can send you the complete code if you want
Thanks in advence!
This issue is know as circular dependency. Long story short, two objects should never have another as its subobject.
Instead you have two options. One is to have one point to another
class Credit : public Menu
{
private:
MainMenu* mainMenu;
};
Or, you could have a manager of sorts, where only the manager is aware of both objects, and let them interact through the manager
class Manager
{
private:
Credit credit;
MainMenu mainMenu;
};
For the technical reason why you can't have a member of a declared but not defined type is because it is a incomplete type, and you can't have an object of incomplete type.
For a c++ application which I'm currently being busy to develop, I have several classes which I need to access through my entire code, without creating a new object
So searching I have found that one of methods that can be used is with the extern linkage specifier.
I would like to know what is best way to use this extern method, I wrote a little sample code
classone.h
#ifndef CLASSONE_H
#define CLASSONE_H
class ClassOne
{
public:
ClassOne();
void showClassOneInt();
private:
int m_classOneInt;
};
extern ClassOne *classOne;
---------------------------------------
classone.cpp
#include "classone.h"
#include <QDebug>
ClassOne *classOne;
ClassOne::ClassOne()
{
m_classOneInt = 1;
}
void ClassOne::showClassOneInt()
{
qDebug() << "ClassOneInt: " << m_classOneInt;
}
---------------------------------------
classtwo.h
#ifndef CLASSTWO_H
#define CLASSTWO_H
class ClassTwo
{
public:
ClassTwo();
void showClassTwoInt();
private:
int m_classTwoInt;
};
#endif // CLASSTWO_H
---------------------------------------
classtwo.cpp
#include "classtwo.h"
#include <QDebug>
ClassTwo::ClassTwo()
{
m_classTwoInt = 2;
}
void ClassTwo::showClassTwoInt()
{
qDebug() << "ClassTwoInt: " << m_classTwoInt;
}
---------------------------------------
classthree.h
#ifndef CLASSTHREE_H
#define CLASSTHREE_H
class ClassThree
{
public:
ClassThree();
void showClassThreeInt();
private:
int m_classThreeInt;
};
#endif // CLASSTHREE_H
---------------------------------------
classthree.cpp
#include "classthree.h"
#include <QDebug>
ClassThree::ClassThree()
{
m_classThreeInt = 3;
}
void ClassThree::showClassThreeInt()
{
qDebug() << "ClassThreeInit: " << m_classThreeInt;
}
---------------------------------------
classtest.cpp
#include "classtest.h"
#include "classone.h"
#include "classtwo.h"
#include "classthree.h"
//Class one pointer already in header
//Class two
extern ClassTwo *classTwo;
//Class three
extern ClassThree *classThree;
ClassTest::ClassTest()
{
//Execute class one
classOne->showClassOneInt();
//Execute class two
classTwo->showClassTwoInt();
//Execute class three
classThree->showClassThreeInt();
}
---------------------------------------
main.cpp
#include <QCoreApplication>
#include "classone.h"
#include "classtwo.h"
#include "classthree.h"
#include "classtest.h"
//Class one pointer already in header file
//Class two pointer
ClassTwo *classTwo;
//Class three pointer
ClassThree *classThree;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//Create object for class one
classOne = new ClassOne;
//Create object for class two
classTwo = new ClassTwo;
//Create object for class three
ClassThree three;
classThree = &three;
//Create a classTest object
ClassTest test;
return a.exec();
}
Please could you tell me what is the best way, thanks for you help.
The best way is to not do it and instead use dependency injection.
If you choose to do it anyway, you should at least use getter/factory functions (i.e. ClassOne &getClassOne())) so you can:
be sure random code can't change the objects and
handle order of construction implicitly by constructing on first use (sometimes appropriate, sometimes not).
Having a global state is generally not a great idea, seek to eliminate it.
If that cannot be done, try the singleton pattern.
class Singleton
{
Singleton(); //keep constructors private to avoid creation by others
static Singleton inst;
public:
static Singleton& Instance() {return inst;}
};
Singleton Singleton::inst;
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.
I am trying to figure out how I am supposed to use enumerations in Qt so that I pack them in a QVariant and convert them to a QJsonValue and later JSON.
Following the docs I ended up declaring my enums:
enum BeautifulColors { Red, Green, Blue };
Q_DECLARE_METATYPE(BeautifulColors);
That way I can use setValue(..) on QVariant to set my custom defined enums as value.
The problem however is QJsonValue::fromVariant(), the docs says:
Converts variant to a QJsonValue and returns it. (...)
For all other QVariant types a conversion to a QString will be
attempted. If the returned string is empty, a Null QJsonValue will be
stored, otherwise a String value using the returned QString.
The conversion to QString fails and and my QJsonValue object ends up being Null.
Following the documentation further is confusing: There is a Q_EUM macro for enumeration definition within QObject. However since QObject is non-copy able I don't think QVariant is supposed to hold it. There are certainly some hacky was to get it working, but that is not what I am looking for. What is the recommended way in Qt to define enums so that they can be used as datatypes and converted into JSON and read from JSON?
Update
Tried the following:
rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include <QObject>
class Rectangle : public QObject
{
Q_OBJECT
public:
enum Color
{
Red,
Green,
Blue,
};
Q_ENUM(Color)
Rectangle(double x, double y, Color color, QObject *parent = 0);
private:
double _x;
double _y;
Color _color;
};
#endif
rectangle.cpp
#include "rectangle.h"
Rectangle::Rectangle(double x, double y, Rectangle::Color color, QObject *parent)
: QObject(parent)
, _x(x)
, _y(y)
, _color(color)
{
}
main.cpp
#include <QVariant>
#include <QDebug>
#include <QString>
#include "rectangle.h"
int main(int argc, char *argv[])
{
int id = qMetaTypeId<Rectangle::Color>();
Rectangle::Color blueColor = Rectangle::Blue;
QVariant myVariant;
myVariant.setValue(blueColor);
qDebug() << id;
qDebug() << myVariant.toString();
}
Now it has a typ id and a string representation! But not the class holding it:
int idRectangle = qMetaTypeId<Rectangle>();
Does not compile and I cannot register it with Q_DECLARE_MEATYPE, because it does not have a constructor. What if I need QVariants toString() to work with any class?
Second Update
Using the Q_GADGET macro I now get a (different) type id for the enum and the class holding it. However I still only get a string representation for the enum.
Q_ENUM or Q_ENUMS are needed to generate the necessary QMetaEnum structure to toString/fromString functionality.
They need to be placed in a QObject derived class with the Q_OBJECT marker or in any class with the Q_GADGET marker in order for moc to process them and generate the necessary code.
The class they are defined in is not the one being stored in the variant though when you store an enum value.
You can consider this class more like a "namespace" for your enum.
I figured the rest out, to be able to use QVariant::toString() a conversion must be registered for the type held by the QVariant.
This was added by KDAB in Qt 5.2 (http://log.cedricbonhomme.org/55/66102.html) but is nowhere mentioned in the doc QVariant::toString()! :(
Anyways it also works for plain enums, the following example will output "Apple"
enum Fruit {
Apple,
Pear,
Orange
};
Q_DECLARE_METATYPE(Fruit)
QString Fruit2QString(Fruit fruit)
{
switch(fruit)
{
case Apple:
return "Apple";
case Pear:
return "Pear";
case Orange:
return "Orange";
}
return "asdf";
}
int main(int argc, char *argv[])
{
std::function<QString(Fruit)> f = &Fruit2QString;
bool success = QMetaType::registerConverter<Fruit, QString>(f);
Q_ASSERT(success);
QVariant v;
v.setValue(Fruit::Apple);
qDebug() << v.toString();
}
Basically I am trying to inherit everything from friction into base (or even the other way round) however, it is not identifying the classes I put in.
base.h
#ifndef BASE_H
#define BASE_H
#include <QMainWindow>
namespace Ui {
class Base;
}
class Base : public QMainWindow{
Q_OBJECT
public:
explicit Base(QWidget *parent = 0);
~Base();
private:
Ui::Base *ui;
};
#endif // BASE_H
friction.h:
#ifndef FRICTION_H
#define FRICTION_H
class Friction : public Base{ // THIS IS WHERE THE ERROR IS
public:
Friction();
};
#endif // FRICTION_H
base.cpp
#include "friction.h"
#include "base.h"
#include "ui_base.h"
Base::Base(QWidget *parent) :QMainWindow(parent),ui(new Ui::Base){
ui->setupUi(this);
}
Base::~Base(){
delete ui;
}
friction.cpp
#include "friction.h"
#include "base.h"
#include "ui_base.h"
Friction::Friction(){
}
and finally main.cpp
int main(int argc, char *argv[]){
QApplication a(argc, argv);
Base w;
w.show();
Friction f;
return a.exec();
}
I receive the error "expected class name before '{' token", I have cut the project down as much as I can and the error still comes up and I really don't know why.
I am fairly new to c++ however I find inheritance not much of an issue on a basic program but upon moving to Qt I couldn't seem to get it working. I have tried numerous things regarding changing the includes etc etc as I am completely oblivious as to why it's not identifying the class.
If friction inherits Base , than you should put:
#include "base.h"
in friction.h file , like so:
#ifndef FRICTION_H
#define FRICTION_H
#include "base.h"
class Friction : public Base{ // THIS IS WHERE THE ERROR IS
public:
Friction();
};