c++ Inheritance Basics in Qt - c++

I wanted to extend a class with an additional property and then assign a base class object to this new class. Here is my sample code:
class QExtFileInfoList: public QFileInfoList
{
public:
bool newProperty;
}
QDir var1 = "/abc/def";
QFileInfoList var2 = var1.entryInfoList(); // works
QExtFileInfoList var3 = var1.entryInfoList(); // does not work
the code returns:
conversion from 'QFileInfoList {aka QList<FileInfo>}'
to non-scalar type 'QElfFileInfoList' requested
How can I fix this issue?

you have defined a new class. But who is creating them?
QDir will not create classes for you that you have created.
There are 3 solutions in mind:
write a converter function QFileInfoList to QElfFileInfoList
write a constructor for QElfFileInfoList taking just a QFileInfoList reference ( this would convert automatically if you would ask for conversion of a single instance)
derive a new class from QDir that is creating your own class for you.
I would choose them in these order.

Related

Using polymorphism with vectors in C++. I'm getting errors when attempting to add a pointer to a derived class to a vector of type pointer to Base

I'm new to C++ and could really appreciate some help. I have a Base class and several class types derived from this. I'm trying to use polymorphism to add these derived objects to a vector of type Base class. After spending some time looking into this, it seems as though the recommended way to do this is using pointers. However, when I try and implement this I get an error message (in intellisense) stating that there is no known conversion between the base and derived class types.
I've included some example code for what I'm trying to achieve. Apologies for such a silly / trivial example, but I'm not allowed to show any of the actual code I'm working on. In this example, in the header file I have an abstract base class "Meal", and two types of derived classes : "SingleCourseMeal" and "ThreeCourseMeal". In the "Restaurant" class I have a vector of type unique_ptr<Meal>
In the cpp file I read from file and create a unique_ptr of either a SingleCourseMeal or a ThreeCourseMeal type, depending on the information in the file. I assign the relevant variables to the dereferenced pointer (from information in the file), and finally add the unique_ptr to the unique_ptr vector in the Restaurant object. However, I'm unable to add a pointer to a ThreeCourseMeal or a SingleCourseMeal to the unique_ptr<Meal> vector.
Could anyone advise where I'm going wrong, and/or how best to implement polymorphism like this? (i.e. populating a vector of type Base with Derived objects)
header file:
class Meal
{
public:
String cuisineType;
double prepTime;
virtual void setRecipe() = 0;
};
class MultipleCourseMeal : public Meal
{
public:
Vector<String> courses;
double intervalBetweenCourses;
void setRecipe() override {}
};
class SingleCourseMeal : public Meal
{
void setRecipe() override {}
};
class Restaurant
{
Vector<std::unique_ptr<Meal>> meals;
};
cpp file:
String cuisineType = getCusineTypeFromFile();
double prepTime = getPrepTimeFromFile();
if (numberOfCourses > 1) {
std::unique_ptr<MultipleCourseMeal> meal = std::make_unique<MultipleCourseMeal>();
Vector<String> courses = getCoursesFromfile();
double intervalBetweenCourses = getIntervalFromFile();
meal->cuisineType = cuisineType;
meal->prepTime = prepTime;
meal->courses = courses;
meal->intervalBetweenCourses = intervalBetweenCourses;
Restaurant.meals.push_back(meal);
}
else {
std::unique_ptr<SingleCourseMeal> meal = std::make_unique<SingleCourseMeal>();
meal.cuisineType = cuisineType;
meal.prepTime = prepTime;
Restaurant.meals.push_back(meal); //THIS IS WHERE THINGS GO WRONG
}

Qt and C++: Overwrite or Expand Class

I want to add some properties (like an ID) to a QPushButton. Therefore, I need to expand or overwrite the class Q_WIDGETS_EXPORT QPushButton : public QAbstractButton
How do I do that?
Thanks for the help.
you dont need to extend the class to just put an id in it ... instead make use of the property system.
as specified in the official doc here:
A property can be read and written using the generic functions QObject::property() and QObject::setProperty(), without knowing anything about the owning class except the property's name.
you just have to do:
ui->myButton->setProperty("Id", 123456);
can also be another object e.g a string (or even your own class if you define it to do that)
ui->myButton->setProperty("_name", "123456");
to read the property is the method property() there for you but read the doc because you get a QVariant as return example:
QVariant(int, 123456)
It really depends on the use case. There is no problem (and often the intended way) in inheriting from Qt-(Widget) Classes (correct me, if I am wrong).
So you could do:
class MyQPushButton : public QPushButton
{
Q_OBJECT
public:
MyQPushButton() : QPushButton(...) {}
private:
int ID = -1;
}
Qt has a very good documentation and you can look at the sources to see what to override.
You could also extend a new class with QPushButton, but than you always have to deal with the QPushButton reference in your class, if you want e.g. connect something. In the inherited class you can connect the slots and so on. But for example you could do this:
class MyQPushButton
{
public:
MyQPushButton() {}
const QPushButton& const GetQPushButton() { return pushButton; }
const QPushButton* const GetQPushButtonPtr() { return &pushButton; }
private:
QPushButton pushButton;
int ID = -1;
}
There is no right and wrong. But I would use the inheritance for Qt-classes.

Get QMetaObject for a subclass of a QObject based on a string of the className

I'm writing an application that loads plugins, and each plugin acts as a factory for QObject subclasses. The application sends parameters to a plugin to instantiate an object, and each object uses different parameters to be created.
This is all working pretty well, however the parameters change between plugins and objects, and it would be nice if the application had a mode to report the parameters needed to construct an object. Objects are created by a config file, so the author of the config needs to know the required parameters to provide, especially if they change.
So I've been looking into Qt's MetaObject system, which is perfect for this use case, because I can create a message that will report the shape of an object's constructor using Q_INVOKABLE on the constructor and QMetaObject::constructor(int i).
The problem is that I have to use the actual class to get a QMetaObject static instance, and I would like to loop over a list of class names as strings to create this report.
I'm not interested in registering types for use with QVariant, because these objects aren't for copying around or sending over a network or serializing. They may be things connected to hardware or consume lots of memory. All I want is the constructor definition of a plugin's factory objects, but originating from the string name of each class the plugin can construct.
QObject instances provide a QMetaObject, but they don't seem to have an innate QMetaType, which is necessary for:
int typeId = QMetaType::type("MyClass");
const QMetaObject *s = QMetaType::metaObjectForType(typeId);
So instead I have to use
QMetaObject m = MyClass::staticMetaObject;
Which requires each definition to be manually coded into the plugin, as mentioned: How can I find a Qt metaobject instance from a class name?
Because QObject doesn't have a copy constructor, Q_DECLARE_METATYPE() will fail, as well as qRegisterMetaType(), and creating a fake copy constructor to satisfy the compiler is a bad idea, and requiring default constructors are bad as well in this use case.
If the solution is to manually type each class into a function providing this message to the config writer, then it is what it is. However if I'm missing a simpler solution, given that the plugin already provides a string list of available objects and acts as a factory for QObject subclasses (that don't have default constructors), I would like to learn it!
Does someone have a better idea how to do this:
QStringList p;
p << "MyClass";
p << "MyClass2";
for(int pi=0;pi<p.length();pi++)
{
int typeId = QMetaType::type(p[pi]);
const QMetaObject *s = QMetaType::metaObjectForType(typeId);
int c = s->constructorCount();
printf("constructor count: %d\n",c);
for (int i=0;i<c;i++)
{
QMetaMethod m = s->constructor(i);
printf("ctor %d: %s %s %d\n",i,qPrintable(m.name()),qPrintable(m.methodSignature()),m.parameterCount());
int nc = m.parameterCount();
QList<QByteArray> pn = m.parameterNames();
QList<QByteArray> pt = m.parameterTypes();
for (int j=0;j<nc;j++)
{
printf("\t%s %s\n",qPrintable(pt.at(j)),qPrintable(pn.at(j)));
}
}
}
This will crash because typeId == QMetaType::UnknownType, and therefore s == null.
Ultimately, manually coding each entry was my solution.

Passing a vector<string> as a parameter in the parameterized constructor of an object

I've got an assignment that I am working on, so I am hesitant of posting all of my code on here (for the off chance that my professor somehow would find it - the policy is that would constitute as cheating).
My issue comes at this though: I've got a visual C++ program that has a textbox that takes a user inputs a string, clicks a button, and adds that string to a vector. I then need to pass that vector (full of strings) into a newly created object via its parameterized constructor. There is also some inheritance involved.
basically, in the .h file for the object and the forms .h file, I have the following (as per how the teacher wanted it to be in the object .h file that I am not allowed to change):
typedef vector<string> StringList;
I then create a new parent object and a new vector in the form's .h file:
private: Trial * t;
private: StringList * strings;
then initialize it in the form's constructor:
strings = new StringList;
I then try to create a new instance of a derived class and assign it on the parent object, while passing the required parameters:
t = new Vector_Trial( strings, multi, type );
I get a red line underneath the first parenthesis that says:
Error: no instance of constructor "Vector_Trial::Vector_Trial" matches the argument list
argument types are:(StringList *, int, Trial::TrialType)
The following are the constructors for the parent class and the derived class:
Trial ( StringList & s, int num_runs, TrialType t )
: strings(s), numRuns(num_runs), type(t) { }
Vector_Trial ( StringList & s, int num_runs, TrialType t ) : Trial ( s, num_runs, t ) {}
I'm assuming my issue is with the vector.. Any ideas?
If you use this function,
Vector_Trial ( StringList & s, int num_runs, TrialType t )
You will pass to it a StringList object, not a pointer to StringList.
Should you change your strings declaration as below,
private: StringList strings;
I don't see why you need to make strings a StringList*.
Instead, just declare it as StringList strings;, and remove the call to new.

Create class instance based on a string input

Background:
In my game engine I have a generic 'script parser' which is used to create game entities by parsing a script file. So in code you would have something like MyEntity* entity = MyScriptParer::Parse("filename.scr");
Any class which is to be scriptable inherits from a generic base class. Internally in the game engine there are some specific classes that use this - particles, fonts etc and this all works nicely in the parser - see extract below
std::string line;
std::getline(ifs, line);
if (line == "[FONT]") {
CFont* f = new CFont();
f->readObject(ifs);
}
else if (line == "[PARTICLE]") {
CParticle* p = new CParticle();
p->readObject(ifs);
}
...
My problem comes with how to handle user defined classes i.e classes in the games that use the game engine. The base class has an abstract method readObject so anything which inherits must implement this method.
The issue is how would the parser know about the new class? E.g say I have a CVehicle class the parser would now need to know to recognise "[VEHICLE]" and also be able to create a new CVehicle
Is there any way to store a class type or something in an array/map so maybe I could have a function to register a list of class types with strings to provide a lookup for creating the new instances?
Bit of a long shot and may not be possible so if anyone has other suggestions on how to approach the parsing they will be welcomed
You can store a class type in an array/map via std::type_info
However, you cannot create a type from this, as it would require more RTTI than is available in C++. (like reflection in .NET).
However, you could store a function pointer to a class factory in such a map.
I.e.
typedef CBaseClass* (*pfnCreateClass)();
std::map<std::string, pfnCreateClass> mapCreate;
// Registering
// CMyCustomClass::GetClass() is a static method that creates a CMyCustomClass
mapCreate.insert(std::pair<std::string, pfnCreateClass>("[CUSTOM_CLASS]", CMyCustomClass::GetClass));
// Get class
std::map<std::string, pfnCreateClass>::const_iterator it = mapCreate.find(line);
if(mapCreate.end() != it)
{
CBaseClass *p = it->second();
p->readObject(ifs);
}
Just have the function to register a new type take in the name of the type and a function for creating the type.
Something like so:
void RegisterType( std::string name, std::function< BaseType() > createFunc );
When registering a new type you do it like so:
RegisterType( "Vehicle", [](){ return new CVehicle; } );
That way the parser can create all the derived types.