Copying a const complex data type
-
Hi,
What complex type is it ?
What exact error do you have ? -
@devDawg said in Copying a const complex data type:
What is the best way to add Data to a Container while avoiding the const qualifier errors from the append() method? What Container should I be using?
Being fairly new to C++, I have been having trouble adding items to my various containers; whenever I try to append something to my QList, I either run into a dropped qualifier exception or an invalid initialization.
when const_cast<>() didn't offer any results, I tried looking to different Container types;
Rather than searching for a "trouble-free container", I highly recommend you spend some time to understand the root problem. For example, a class/struct can only be added to a QList by-value if it has a copy-constructor.
As @SGaist said:
- Please show the header file for your class.
- What exactly are the error messages that you get?
if T & var declares an address of type T for var,
T& is a reference, not an address!
void func1(MyData & var); // Function that takes a reference to a MyData instance void func2(MyData * var); // Function that takes a pointer/address to a MyData instance
func1 and func2 can be used to do similar things, but C++ programmers use them quite differently by convention. Please spend some time reading about pointers vs references.
what does T&& var declare? The address of the pointer pointing to the address of var?
T&& is an rvalue reference.
void func3(MyData && var); // Function that takes an rvalue reference to a MyData instance void func4(MyData ** var); // Function that takes a pointer-to-a-pointer to a MyData instance //
While func1 and func2 are kind of similar, func3 and func4 have absolutely nothing in common.
See https://blog.smartbear.com/development/c11-tutorial-explaining-the-ever-elusive-lvalues-and-rvalues/ as a starting point.
-
I agree; I would much rather learn the root of the issue then guess, but coming from Java, C, & Python, all this pointer and reference business is strange. But yes, I definitely need to read more.
So a day ago, I was getting this huge stack trace that pointed back to a method in DataModel:
void DataModel::addModule(DataModule mod_) { if (std::is_const<decltype (mod_)>::value) { modules.append(mod_); } else { const DataModule holder(mod_); mod_.~DataModule(); modules.append(holder); } }
NOW, I'm getting "no matching constructor for initialization of 'DataModule'", as well as
C:\Users\AMcFarlane\Qt\projects\UiTest\datamodel.cpp:119: error: no matching function for call to 'DataModule::DataModule(DataModule&)' this->addModule(mod); ^ within this method when I call "this->addModule(mod);".
Note that I was also getting trouble earlier with the line
mod.addData(piece);
:DataModel::DataModel(const QString fileName) { QFile file(fileName); if (!file.open(QIODevice::OpenModeFlag::ReadOnly)){ qDebug() << "file will not open: "<<file.errorString()<<endl; } QTextStream stream(&file); QString line = stream.readLine(); int i = 0; while (line.at(0)!='%') { i++; qDebug() << i << endl; QChar first = line.front(); if (first == '/') { //next line; we ignore this one. line = stream.readLine(); } else if (first == '#') { //marks the start of a module line.remove(0,1); DataModule mod(this); mod.setName(line); qDebug() << "name: " << mod.Name() << endl; //here we will load the data pieces into the module. Thus, we go until the first char is '' or '#'. line = stream.readLine(); while (line.at(0)!='#'&&line.at(0)!='%') { qDebug() << "line: " << line << endl; if (line.at(0)!='-'){ qDebug() << "Error reading data piece. Check the file, and check your method"; break; } QStringList parts = line.split(",",QString::SplitBehavior::KeepEmptyParts,Qt::CaseSensitivity::CaseSensitive); DataPiece piece(this); parts.first().remove(0,1); //getting rid of the first '-' QStringList prior = parts.at(0).split("-",QString::SplitBehavior::KeepEmptyParts,Qt::CaseSensitivity::CaseSensitive); //prior.at(0) will have the priority, and prior.at(1) will have the name. if (prior.at(0).endsWith("1",Qt::CaseSensitivity::CaseInsensitive)){ qDebug() << "loading a Data Piece" << endl; piece.setName(prior.at(1)); qDebug() << piece.Name(); qDebug() << prior.at(1); piece.setValue(const_cast<QString&>(parts.at(1))); qDebug() << piece.Value(); qDebug() <<parts.at(1); piece.setUnits(const_cast<QString&>(parts.at(2))); qDebug() << piece.Units(); qDebug() <<parts.at(2); piece.setMinimum(const_cast<QString&>(parts.at(3))); qDebug() << piece.Minimum(); qDebug() <<parts.at(3); piece.setMaximum(const_cast<QString&>(parts.at(4))); qDebug() << piece.Maximum(); qDebug() <<parts.at(4); piece.setPriority(const_cast<QString&>(prior.at(0))); qDebug() << piece.Priority(); qDebug() <<prior.at(0); piece.setWarnMin(const_cast<QString&>(parts.at(5))); qDebug() << piece.warnMin(); qDebug() <<parts.at(5); piece.setWarnMax(const_cast<QString&>(parts.at(6))); qDebug() << piece.warnMax(); qDebug() <<parts.at(6); piece.setCritMin(const_cast<QString&>(parts.at(7))); qDebug() << piece.critMin(); qDebug() <<parts.at(7); piece.setCritMax(const_cast<QString&>(parts.at(8))); qDebug() << piece.critMax(); qDebug() <<parts.at(8); } qDebug() << "here" << endl; **mod.addData(piece);** qDebug() << "data piece loaded" << endl; line = stream.readLine(); } mod.initTextBody(); **this->addModule(mod);** //ERROR IS HAPPENING AT THIS LINE } else { qDebug() << "Error: Parse out of order ~ "+line; } } qDebug() << "stream done" << endl; }
datamodel.h:
#ifndef DATAMODEL_H #define DATAMODEL_H #include "datamodule.h" #include <QDebug> #include <QString> #include <QFile> #include <QObject> /*This class will be responsible for one specific module. Depending on the page, this model will contain a select number of params. * For example, on the summary page, we only want a brief overview of Priority 1 measurements, so the data models will be less compact. * Whereas on the following pages that detail full modules, the models will be larger. */ class DataModel: public QObject { friend class QListView; Q_OBJECT Q_PROPERTY(int size READ getSize) public: explicit DataModel(QObject * parent = nullptr); explicit DataModel(DataModel &other); explicit DataModel(const QString file); //this is our main constructor here. DataModule& getModuleAt(int i); void addModule(DataModule mod_); const QVector<DataModule> getModel(); const Q_INVOKABLE QString tester(); int getSize(); ~DataModel(); private: QVector<DataModule> modules; //separates Modules into Rectangles. Spacing modifiable. int spacing; int size; }; #endif // DATAMODEL_H
datamodule.h:
#ifndef DATAMODULE_H #define DATAMODULE_H #include <QVector> #include "datapiece.h" /*A module, in this case, consists of a list of DataPieces. */ class DataModule: public QObject { Q_OBJECT Q_PROPERTY(QString textBody READ getTextBody) public: explicit DataModule(QObject * parent = nullptr); explicit DataModule(DataModule&); //means no implicit type conversions are performed. explicit DataModule(const DataModule&); explicit DataModule(DataModule&&); DataModule& operator=(const DataModule& thing); DataModule& operator=(DataModule& thing); DataModule& operator=(DataModule&&); DataModule& operator=(const DataModule&&); const Q_INVOKABLE QString getTextBody(); //usable in QML int numOfPieces(); const QString Name() const; QString Name(); const QVector<DataPiece> Data() const; QVector<DataPiece> Data(); void setName(QString name_); void setName(const QString name_) const; void setTextBody(QString body); //can use this for testing purposes. void setTextBody(const QString body) const; void setData(const QVector<DataPiece> data_) const; void setData(QVector<DataPiece> data_); void initTextBody(); //forms the textBody of the Module with the DataPieces on hand. void addData(const DataPiece& piece); void addData(DataPiece& piece); ~DataModule(); private: QString name; QString textBody; //the text that will appear in the DataModel. These will be inserted into Rectangles, which will then be spaced. QVector<DataPiece> data; }; #endif // DATAMODULE_H
datapiece.h:
#ifndef DATAPIECE_H #define DATAPIECE_H #include <QString> #include <QObject> class DataPiece: public QObject { Q_OBJECT Q_PROPERTY(QString value READ Value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(QString units READ Units WRITE setUnits) Q_PROPERTY(QString min READ Minimum WRITE setMinimum) Q_PROPERTY(QString max READ Maximum WRITE setMaximum) Q_PROPERTY(QString name READ Name WRITE setName) Q_PROPERTY(QString priority READ Priority WRITE setPriority) Q_PROPERTY(QString warnMax READ warnMax WRITE setWarnMax) Q_PROPERTY(QString warnMin READ warnMin WRITE setWarnMin) Q_PROPERTY(QString critMax READ critMax WRITE setCritMax) Q_PROPERTY(QString critMin READ critMin WRITE setCritMin) /* * Some of these property types may potentially be changed in the future (unsigned int, int, float, double, qreal) */ /*Out of all these properties, the only one that needs a slot is the value. */ public: explicit DataPiece(QObject *parent = nullptr); //makes it known that this DataPiece is linked to a parent object in QML. explicit DataPiece(DataPiece&); explicit DataPiece(const DataPiece&); explicit DataPiece(DataPiece&&); //The only reason this might be necessary is for overriding the QObject copy assignment operator. DataPiece& operator=(const DataPiece&); //I think these are necessary. DataPiece& operator=(DataPiece&&); DataPiece& operator=(const DataPiece&&); void setValue(QString val); void setValue(const QString val) const; void setUnits(QString units_); void setUnits(const QString units_) const; void setMinimum(QString max); void setMinimum(const QString max) const; void setMaximum(QString min); void setMaximum(const QString min) const; void setName(QString name_); void setName(const QString name_) const; void setPriority(QString prior_); void setPriority(const QString prior_) const; void setWarnMax(QString warnmax); void setWarnMax(const QString warnmax) const; void setWarnMin(QString warnmin); void setWarnMin(const QString warnmin) const; void setCritMax(QString critmax); void setCritMax(const QString critmax) const; void setCritMin(QString critmin); void setCritMin(const QString critmin) const; // void setUserOutput(); const QString Name() const; QString Name(); const QString Value()const; QString Value(); const QString Units()const; QString Units(); const QString Minimum()const; QString Minimum(); const QString Maximum()const; QString Maximum(); const QString Priority()const; QString Priority(); const QString warnMax()const; QString warnMax(); const QString warnMin()const; QString warnMin(); const QString critMax()const; QString critMax(); const QString critMin()const; QString critMin(); ~DataPiece(); public slots: void onValueChanged(const QString newVal); //for the future, may need to have slots for different types of ints (unsigned/signed, 8bit, 16bit, 32bit, 64bit) signals: void valueChanged(); private: QString name; QString value; QString units; QString minimum; QString maximum; QString priority; QString warnMaximumLevel; QString warnMinimumLevel; QString critMaximumLevel; QString critMinimumLevel; QString userOutput; }; #endif // DATAPIECE_H
I will check out that link, as well as any other helpful documentation you might suggest. I've just started using C++ within the past few weeks, and need to get up to speed as quickly as possible.
-
@devDawg said in Copying a const complex data type:
class DataModule: public QObject
First things first: A
DataModule
is a QObject.QObjects cannot be copied. Remember this well! These sections explain why:
void addModule(DataModule mod_); ... this->addModule(mod);
This cannot work.
addModule()
takes a DataModule object by-value, which means the function will try to copy the DataModule object.As we learnt before, QObjects cannot be copied. Therefore, you cannot pass a QObject into a function by-value.
Instead, pass QObjects by-pointer:
void addModule(DataModule *mod_);
QVector<DataModule>
This cannot work either. Containers can only hold copyable objects.
As we learnt before, QObjects cannot be copied. Therefore, you cannot store a QObject inside a container.
Instead, store QObject pointers inside a container:
QVector<DataModule*>
NOW, I'm getting "no matching constructor for initialization of 'DataModule'", as well as C:\Users\AMcFarlane\Qt\projects\UiTest\datamodel.cpp:119: error: no matching function for call to 'DataModule::DataModule(DataModule&)'
As we learnt before, QObjects cannot be copied. Therefore, they do not have copy-constructors. This is what the error is trying to tell you.
coming from Java, C, & Python, all this pointer and reference business is strange.
You are probably already familiar with pointers from C. In Java, passing objects into functions always involves references -- it's just not obvious because Java doesn't use
&
.C++ is kind of a middle-ground between C and Java. With a bit of reading and experience, you will start to easily recognize C++ concepts that you already knew from C and Java.
See the References are like pointers section.
Good luck!
P.S. In future posts, please do the following to make it easier for us to understand your problem:
- Be systematic and focussed in your descriptions.
- Your previous post had "So a day ago, I was getting this huge stack trace that pointed back to a method in DataModel… NOW, I'm getting "no matching constructor for initialization of 'DataModule'"... Note that I was also getting trouble earlier with the line "mod.addData(piece);""
- You described 3 unrelated problems, but 2 of them aren't present in your code. This made your post very unclear.
- Format your code properly. Put 3 backticks without spaces (`) before and after your code.
- Simplify your code. Leave out irrelevant bits.
- Be systematic and focussed in your descriptions.
-
JKSH, Mr. Moderator, I thank you very much for your patience and understanding. This is my first time stepping on a developer forum, so I appreciate you briefing me on the common post practices.
After reading your original link, a few other links, and the piece about QObjects not being copyable, my mistakes are really starting to make sense.
I am glad people like you exist in the world :)
-
Believe me, in C++ it's easy to get shout at. I speak from experience
- as a general rule, in C++ if you explicitly call the destructor (
mod_.~DataModule();
) you are wrong unless proven otherwise.- The destructor gets called automatically when a variable allocated on the stack goes out of scope and you don't want it called twice.
- as a general rule, in C++ if you use
mutable
/const_cast
(piece.setValue(const_cast<QString&>(parts.at(1)));
) you are wrong unless proven otherwise.- In this case you didn't even need it
- notable exception are mutexes
develop a QML model from C++
Define "model". Are we talking about model/view?
- as a general rule, in C++ if you explicitly call the destructor (
-
@VRonin Something of the sort, yeah. None of the existing QML models allowed me to use HTML formatting within my text bodies (I could very well have just been doing it the wrong way), so I decided to define my own DataModel that will eventually be receiving data values from various nodes within the CANopen (controller area network) protocol interface.
Before I get to importing data from the network, I must first set up my Model, get it to build, then insert dummy values that change with time to see it visually working properly.
Thank you for the tips! Duly noted. Merci beaucoup ;)
-
@devDawg said in Copying a const complex data type:
None of the existing QML models allowed me to use HTML formatting within my text bodies
The model has almost nothing to do with how the data is displayed. what you want is a delegate. In QML is much easier to have complex delegates that do whatever you want. See https://qmlbook.github.io/en/ch06/index.html#delegate and https://wiki.qt.io/How_to_Use_a_Custom_Class_in_C%2B%2B_Model_and_QML_View#Using_the_data_in_QML
-
@VRonin With that being said, I feel really stupid... I don't think there was every any need to even make my own model. By communicating with the delegate and specifying rich text format, I could have gotten everything I wanted within QML.. I am so close to finishing this, however, that I may as well, and I have also learnt a heck of a lot along the way. So, time well spent.
On that same thought, if I was going to import data from a source, whether it be a bus stream or a data simulator, would the best plan be to declare that data source as a C++ class, and then use that class as the root context for the QML Display?