Quite often I find myself in need of some custom scheme model, mandating the implementation of more and more models, made even more tedious by the inability of QObject derived classes to be templates.
Qt has the QStandardItemModel but that seems a bit verbose and inconvenient to use, especially from the qml side, and total overkill for a basic list model.
There is also the basic qml ListModel, but that is limited and not elegant to use on the C++ side, and I do suspect a tad more bloated than it needs to be.
Qt has QVariant, which is what its model/view architecture uses internally, so it is surprising that the framework doesn't provide something as simple as:
// qml code
VarMod {
roles: ["name", "age", "weight"]
Component.onCompleted: {
insert(["Jack", 34, 88.5], -1) // qml doesn't support
insert(["Mary", 26, 55.3], -1) // default arg values
}
}
// cpp code
VarMod vm { "name", "age", "weight" }; // member declaration
vm.insert({ "Jack", 34, 88.5 });
vm.insert({ "Mary", 26, 55.3 });
And here it is.
Note that you do have to be responsible with the parameters, as there is no type safety, in fact it has implicit analog to ListModel's dynamicRoles - that is, it will accept and work with any QVariant compatible value on every role slot.
As for memory efficiency, consider that QVariant has 8 bytes for data, plus 4 bytes for type id, plus another 4 bytes of padding, for a total of 16 bytes. That is not insignificant if you are using it for small data types, like say bool, so in case you have a data scheme that has a lot of small (1 - 4 bytes) fields and a scores of items, implementing a full model will still be the better option. It is still a lot better than the generic object model I am using, which has to carry the bloat of QObject, and even more significant in the case of qml objects.
Additionally, QVariant being 16 bytes, I opted to not use the convenience of QVariantList for data storage, which has an underlying QList, making the situation worse than it needs to be. Although that is fixed in Qt 6, which gets rid of QList as it is, and replaces it with an alias of QVector. Still, std::vector helps to avoid that in any case, plus it might actually be a tad faster, since it doesn't have to deal with COW and atomic ref counters. There are several auxiliary methods to help with pre-allocation and release of memory as well.
The model has a safeguard against the change the roles for obvious reasons, the latter is primarily intended to be initialized just once, but there is reset() that is intended to be used in a more dynamic qml context, making it possible to redefine the model schema on the fly and provide a compatible delegate. For the sake of certainty, the roles can only be redefined after the model has been explicitly reset.
There is a minute difference in inserting, on the c++ side, the parameter pack is passed wrapped in {}, in qml it is wrapped in [], both leveraging implicit conversion in the context specific way. Also, note that qml currently doesn't support omitting parameters with default values provided on the c++ side, so for appending you do have to provide an invalid index. Naturally, it would be trivial to add convenience methods for appending and prepending if needed.
In addition to the syntax example of the question, it is also possible to add multiple items at once, from "declarative-y" qml structure such as:
let v = [["Jack", 34, 88.5],
["Mary", 26, 55.3],
["Sue", 22, 69.6]]
vm.insertList(v, -1)
Finally, type safety is possible to implement if the scenario really calls for it, then each role can be specified with the expected type to go with it, such as:
VarMod vm {{"name", QMetaType::QString},
{"age", QMetaType::Int},
{"weight", QMetaType::QReal}};
and then iterating and making the necessary checks to ensure type safety when inserting.
Update: I also added serialization, and save/load from disk features, note that this will serialize the data together with the mode schema.
class VarMod : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(QVariantList roles READ roles WRITE setRoles NOTIFY rolesChanged)
QVariantList vroles;
QVariantList roles() const { return vroles; }
QHash<int, QByteArray> _roles;
std::vector<std::vector<QVariant>> _data;
inline bool checkArgs(int rc) const {
if (rc == _roles.size()) return true;
qWarning() << "arg size mismatch, got / expected" << rc << _roles.size();
return false;
}
inline bool inBounds(int i, bool ok = false) const {
if (i > -1 && i < (int)_data.size()) return true;
if (!ok) qWarning() << "out of bounds" << i; // do not warn if intentionally appending
return false;
}
inline bool validRole(int r) const { return (r > -1 && r < _roles.size()); }
protected:
QHash<int, QByteArray> roleNames() const override { return _roles; }
int rowCount(const QModelIndex &) const override { return _data.size(); }
QVariant data(const QModelIndex &index, int r) const override {
r = r - Qt::UserRole - 1;
if (inBounds(index.row()) && validRole(r)) return _data[index.row()][r];
return QVariant();
}
public:
VarMod() {} // for qml
VarMod(std::initializer_list<QByteArray> r) {
int rc = Qt::UserRole + 1;
for (const auto & ri : r) {
_roles.insert(rc++, ri);
vroles << QString::fromLatin1(ri);
}
rolesChanged();
}
inline void insert(std::initializer_list<QVariant> s, int i = -1) {
if (!checkArgs(s.size())) return;
insert(QVariantList(s), i);
}
inline bool setItem(int i, std::initializer_list<QVariant> s) {
if (checkArgs(s.size())) return setItem(i, QVariantList(s));
return false;
}
void setRoles(QVariantList r) {
if (_roles.empty()) {
int rc = Qt::UserRole + 1;
for (const auto & vi : r) _roles.insert(rc++, vi.toByteArray());
vroles = r;
rolesChanged();
} else qWarning() << "roles are already initialized";
}
void read(QDataStream & d) {
reset();
QVariantList vr;
d >> vr;
quint32 s;
d >> s;
_data.resize(s);
for (uint i = 0; i < s; ++i) {
_data[i].reserve(vr.size());
for (int c = 0; c < vr.size(); ++c) {
QVariant var;
d >> var;
_data[i].push_back(std::move(var));
}
}
setRoles(vr);
beginResetModel();
endResetModel();
}
void write(QDataStream & d) const {
d << vroles;
d << (quint32)_data.size();
for (const auto & v : _data) {
for (const auto & i : v) d << i;
}
}
public slots:
void insert(QVariantList s, int i) {
if (!inBounds(i, true)) i = _data.size();
if (!checkArgs(s.size())) return;
beginInsertRows(QModelIndex(), i, i);
_data.insert(_data.begin() + i, { s.cbegin(), s.cend() });
endInsertRows();
}
void insertList(QVariantList s, int i) {
if (!inBounds(i, true)) i = _data.size();
int added = 0;
for (const auto & il : s) {
QVariantList ll = il.value<QVariantList>();
if (checkArgs(ll.size())) {
_data.insert(_data.begin() + i + added++, { ll.cbegin(), ll.cend() });
}
}
if (added) {
beginInsertRows(QModelIndex(), i, i + added - 1);
endInsertRows();
}
}
bool setData(int i, int r, QVariant d) {
if (!inBounds(i) || !validRole(r)) return false;
_data[i][r] = d;
dataChanged(index(i), index(i));
return true;
}
bool setDataStr(int i, QString rs, QVariant d) { // a tad slower
int r = _roles.key(rs.toLatin1()); // role is resolved in linear time
if (r) return setData(i, r - Qt::UserRole - 1, d);
qWarning() << "invalid role" << rs;
return false;
}
bool setItem(int i, QVariantList d) {
if (!inBounds(i) || !checkArgs(d.size())) return false;
_data[i] = { d.cbegin(), d.cend() };
dataChanged(index(i), index(i));
return true;
}
QVariantList item(int i) const {
if (!inBounds(i)) return QVariantList();
const auto & v = _data[i];
return { v.begin(), v.end() };
}
QVariant getData(int i, int r) const {
if (inBounds(i) && validRole(r)) return _data[i][r];
return QVariant();
}
QVariant getDataStr(int i, QString rs) const {
int r = _roles.key(rs.toLatin1()); // role is resolved in linear time
if (r) return getData(i, r);
qWarning() << "invalid role" << rs;
return QVariant();
}
QVariantList take(int i) {
QVariantList res = item(i);
if (res.size()) remove(i);
return res;
}
bool swap(int i1, int i2) {
if (!inBounds(i1) || !inBounds(i2)) return false;
std::iter_swap(_data.begin() + i1, _data.begin() + i2);
dataChanged(index(i1), index(i1));
dataChanged(index(i2), index(i2));
return true;
}
bool remove(int i) {
if (!inBounds(i)) return false;
beginRemoveRows(QModelIndex(), i, i);
_data.erase(_data.begin() + i);
endRemoveRows();
return true;
}
void clear() {
beginResetModel();
_data.clear();
_data.shrink_to_fit();
endResetModel();
}
void reset() {
clear();
_roles.clear();
vroles.clear();
rolesChanged();
}
void reserve(int c) { _data.reserve(c); }
int size() const { return _data.size(); }
int capacity() const { return _data.capacity(); }
void squeeze() { _data.shrink_to_fit(); }
bool fromFile(QString path) {
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) return false;
QDataStream d(&f);
read(d); // assumes correct data
return true;
}
bool toFile(QString path) const {
QFile f(path);
if (!f.open(QIODevice::WriteOnly)) return false;
QDataStream d(&f);
write(d);
return true;
}
signals:
void rolesChanged();
};
I also created this sorting/filtering view to supplement the model:
class View : public QSortFilterProxyModel {
Q_OBJECT
Q_PROPERTY(QJSValue filter READ filter WRITE set_filter NOTIFY filterChanged)
Q_PROPERTY(bool reverse READ reverse WRITE setReverse NOTIFY reverseChanged)
bool reverse() const { return _reverse; }
void setReverse(bool v) {
if (v == _reverse) return;
_reverse = v;
reverseChanged();
sort(0, (Qt::SortOrder)_reverse);
}
bool _reverse = false;
mutable QJSValue m_filter;
QJSValue & filter() const { return m_filter; }
void set_filter(QJSValue & f) {
if (!m_filter.equals(f))
m_filter = f;
filterChanged();
invalidateFilter();
}
}
public:
View(QObject *parent = 0) : QSortFilterProxyModel(parent) { sort(0, (Qt::SortOrder)_reverse); }
signals:
void filterChanged();
void reverseChanged();
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &) const override {
if (!m_filter.isCallable()) return true;
VarMod * vm = qobject_cast<VarMod *>(sourceModel());
if (!vm) {
qWarning() << "model is not varmod";
return true;
}
return m_filter.call({_engine->toScriptValue(vm->item(sourceRow))}).toBool();
}
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override {
VarMod * vm = qobject_cast<VarMod *>(sourceModel());
if (!vm) {
qWarning() << "model is not varmod";
return false;
}
return vm->getData(left.row(), sortRole()) < vm->getData(right.row(), sortRole());
}
};
For sorting, you just have to specify the sorting role, note that it is the index of the "column" rather than the int value from the roles hash. For filtering it works via a qml functor that receives the model item as a JS array, and expects to return a bool, a c++ functor can be easily added via std::function if needed. Also note that it needs a pointer to the actual qml engine.
View {
id: vv
sourceModel: vm
sortRole: sr.value
reverse: rev.checked
filter: { sa.value; o => o[1] < sa.value } // "capturing" sa.value to react to value changes
}
After creating a hash table and assigning each letter to a value for the table, i am noticing the first word output by the table for the beginning of every linked list is the same word. Somehow it seems I am transferring the entire dictionary to each array in the table even though I have attempted to separate them. Any assistance would be awesome! Thanks in advance
// Implements a dictionary's functionality
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char *word;
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 25;
// Hash table
node *table[N];
char lowerword[LENGTH+1];
// Returns true if word is in dictionary else false
bool check(const char *word)
{
int bucketfind = 0;
int x = 0;
for (int b = word[x]; b != '\0';b = word[x], x++)
{
int lowertemp = tolower(word[x]);
if (x == 0)
{
bucketfind = lowertemp - 97;
}
char lowerfinal = lowertemp;
lowerword[x] = lowerfinal;
//printf("%c", lowerword[x]);
}
int wordlen = x + 1;
int pr = 0;
while (table[bucketfind] -> next != NULL)
{
int dwlen = strlen(table[bucketfind]-> word);
pr++;
//printf("%i, %i, %s, %i\n", pr, dwlen, table[bucketfind] -> word, bucketfind);
}
//TODO
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
int asciifirst = word[0];
int lowerfirst = tolower(asciifirst);
int bucketnum = lowerfirst - 97;
return bucketnum;
}
// Loads dictionary into memory, returning true if successful else false
int dictwords = 0;
//char *cword = (char*)malloc(sizeof(char)*46);
bool load(const char *dictionary)
{
char *cword = malloc(sizeof(char)*46);
FILE *dict = fopen(dictionary, "r");
if (dictionary == NULL)
{
return false;
}
int x = 0;
while ((fscanf(dict, "%s", cword) != EOF))
{
node *nword = malloc(sizeof(node));
nword -> word = cword;
nword -> next = NULL;
int bucket = hash(cword);
//printf("%i\n", bucket);
if (table[bucket] != NULL)
{
nword -> next = table[bucket];
table[bucket] = nword;
}
else
{
table[bucket]= nword;
}
dictwords++;
}
fclose(dict);
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
return dictwords;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
// TODO
return false;
}
It's not just the first word; every word in the linked list is the same word (the last one read). cword gets 46 bytes of memory at a specific address here char *cword = malloc(sizeof(char)*46);. Every word from dictionary is read into that same address.
so i have been browsing the previous questions before about this issue, but i could not find a solution for my code.
cpp file of dialog
------------------------------------------------
#include "everesult.h"
#include "ui_everesult.h"
everesult::everesult(QWidget *parent) :
QDialog(parent),
ui1(new Ui::everesult)
{
ui1->setupUi(this);
}
everesult::~everesult()
{
delete ui1;
}
void everesult::setmodel(QStandardItemModel *model)
{
ui1->listView->setModel(model);
}
void everesult::on_buttonBox_clicked(QAbstractButton *button)
{
EveReprocess M_;
QModelIndex Selectedindex = ui1->listView->currentIndex();
QModelIndex StationIdsindex = ui1->listView->model()->index(0, 1);
int typeID = 0;
int stationID = 0;
stationID = ui1->listView->model()->data(StationIdsindex, Qt::DisplayRole).toInt();
typeID = ui1->listView->model()->data(Selectedindex, Qt::DisplayRole).toInt();
M_.GetMaterials(typeID, stationID);
}
--------------------------------------------------
Getmaterial and replyFinished from main window.
--------------------------------------------------
void EveReprocess::GetMaterials(int typeId, int stationid)
{
//get typeid from material list
this->modelMaterial = new QSqlQueryModel();
modelMaterial->setQuery(QString("SELECT tm.quantity, tm.materialTypeID, t.typeName FROM invTypeMaterials tm INNER JOIN invTypes t ON t.TypeID = tm.materialTypeId WHERE tm.TypeID=%1 ").arg(typeId));
if (!modelMaterial->query().exec())
qDebug() << modelMaterial->query().lastError();
//Set eve Central Url with typeids
QUrl url = QUrl("http://api.eve-central.com/api/marketstat?");
QUrlQuery q;
int numRows = modelMaterial->rowCount();
for (int row = 0; row < numRows; ++row)
{
QModelIndex index = modelMaterial->index(row, 1);
q.addQueryItem( QString("typeid"), QString::number(modelMaterial->data(index, Qt::DisplayRole).toInt()));
}
q.addQueryItem( QString("usesystem"), QString::number(stationid));
//set created url and connect
url.setQuery(q);
qDebug() << url;
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply *)));
manager->get(QNetworkRequest(url) );
}
void EveReprocess::replyFinished(QNetworkReply *reply)
{
qDebug() << "replyFinished called";
if ( reply->error() != QNetworkReply::NoError ) {
qDebug() << "Request failed, " << reply->errorString();
emit replyFinished(false);
return;
}
qDebug() << "Request succeeded";
//process with xmlreader and get values
processSearchResult( reply);
}
some of the code is here, i think it should be somewhere here, as the rest works fine.
this issue showed up after i use a dialog to let user pick a int from a list.
below is the function that calls the dialog that i have made for this. sorry about code format wil clean it up after it is working
void EveReprocess::Search_TypeId(QString ItemName, QString SystemName)
{
QList<int> TypeIdList;
QList<int> StationIdList;
modelIds = new QStandardItemModel(10,2,this);
if !(db.isOpen()) return;
this->queryItem = new QSqlQuery;
queryItem->prepare("SELECT typeID FROM invTypes WHERE invTypes.typeName LIKE ? AND invTypes.groupID NOT IN (268,269,270)AND published= 1");
ItemName.prepend("%");
ItemName.append("%");
queryItem->bindValue(0, ItemName);
this->queryStation = new QSqlQuery;
queryStation->prepare("SELECT solarSystemID FROM mapSolarSystems WHERE mapSolarSystems.solarSystemName LIKE ?");
SystemName.prepend("%");
SystemName.append("%");
queryStation->bindValue(0, SystemName);
if(!queryStation->exec() || !queryItem->exec() )
{
qDebug() << queryItem->lastError().text();
qDebug() << queryItem->lastQuery();
qDebug() << queryStation->lastError().text();
qDebug() << queryStation->lastQuery();
}
while( queryStation->next())
{
StationIdList.append(queryStation->value(0).toInt());
}
while(queryItem->next())
{
TypeIdList.append(queryItem->value(0).toInt());
}
for (int i = 0; i < StationIdList.count(); ++i)
{
modelIds->setItem(i,1,new QStandardItem(QString::number(StationIdList.at(i))));
}
for (int i = 0; i < TypeIdList.count(); ++i)
{
modelIds->setItem(i,0,new QStandardItem(QString::number(TypeIdList.at(i))));
}
//
everesult Dialog;
Dialog.setmodel(modelIds);
Dialog.exec();
}
Before you proceed any further, some of your code is allows SQL injections. Even when it's not a security hole, it'll still lead to bugs. Instead of using string substitution in SQL queries, you should be using bindings.
Your problem is here:
everesult Dialog;
Dialog.setmodel(modelIds);
Dialog.exec();
The exec() is a blocking function - it blocks the main event loop until the dialog is dismissed. Thus the signals from the threaded network access manager never get delivered to your objects.
You should display the dialog box asynchronously, like so:
everesult * dialog = new everesult;
dialog->setModel(modelIds);
dialog->show();
connect(dialog, SIGNAL(accepted()), dialog, SLOT(deleteLater());
connect(dialog, SIGNAL(rejected()), dialog, SLOT(deleteLater());
Note that it's misleading to have type names starting with lower case and variable names starting with upper case. Qt's convention is the opposite, and it's useful to retain it unless you have a good reason to do otherwise.
DO Parameter Binding in SQL Queries
QSqlQuery query("SELECT .. WHERE tm.TypeID=:typeid");
query.bindValue(":typeid", typeId);
QSqlQueryModel model;
model.setQuery(query);
DON'T DO String Substitution in SQL Queries
setQuery(QString("SELECT ... WHERE tm.TypeID=%1 ").arg(typeId));
It is not possible to capture an argument that has been passed as reference with a QSignalSpy:
QSignalSpy spy( myObject, SIGNAL(foo(int&)));
...
int& i=spy.at(0).at(0).value<int&>();
Since a QVariant can not contain a reference member. Plain logic.
But are there other solutions to check the passed-in argument?
Since Qt 5, we can simply connect to a lambda function, which makes the use of the QSignalSpy unnecessary:
std::vector<Value> values;
QObject::connect(myObject, &MyObject::foo,
[&](const auto &value)
{ values.emplace_back(value); });
myObject.somethingCausingFoo();
ASSERT_EQ(1u, values.size());
EXPECT_EQ(expectedValue, values.at(0));
An "ugly solution" would be to hack the fairly simple QSignalSpy code in order to handle the reference passed arguments. I provide a minimal working example for int reference arguments. The only changes were made to initArgs and appendArgs functions.
Notice that with this approach you will only be able to check the value of the passed argument by reference. You will not be able to change it's value.
In the initArgs function we check if we have references by argument and we populate the shouldreinterpret list.
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
// Check if we have a reference by removing the & from the parameter name
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
and the appendArgs function, where we reinterpret the passed by reference arguments:
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
list << QVariant(type, &(*reinterpret_cast<int*>(a[i + 1])));
break;
// Do the same for other types
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
Complete code for reference:
class MySignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
MySignalSpy(QObject *obj, const char *aSignal)
{
#ifdef Q_CC_BOR
const int memberOffset = QObject::staticMetaObject.methodCount();
#else
static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
Q_ASSERT(obj);
Q_ASSERT(aSignal);
if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
return;
}
QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
const QMetaObject *mo = obj->metaObject();
int sigIndex = mo->indexOfMethod(ba.constData());
if (sigIndex < 0) {
qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = ba;
initArgs(mo->method(sigIndex));
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
int qt_metacall(QMetaObject::Call call, int methodId, void **a)
{
methodId = QObject::qt_metacall(call, methodId, a);
if (methodId < 0)
return methodId;
if (call == QMetaObject::InvokeMetaMethod) {
if (methodId == 0) {
appendArgs(a);
}
--methodId;
}
return methodId;
}
private:
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
int k = (*reinterpret_cast<int*>(a[i + 1]));
list << QVariant(type, &k);
break;
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
QList<int> args;
// Holds the indexes of the arguments that
QList<bool> shouldReinterpret;
};
Queue class
#ifndef Queue_H
#define Queue_H
#include "Car.h"
#include <iostream>
#include <string>
using namespace std;
const int Q_MAX_SIZE = 20;
class Queue {
private:
int size; // size of the queue
Car carQueue[Q_MAX_SIZE];
int front, rear;
public:
Queue();
~Queue();
bool isEmpty();
bool isFull();
void enqueue(Car c);
void dequeue(); // just dequeue the last car in the queue
void dequeue(Car c); // if a certain car wants to go out of the queue midway.
// Condition: Car is not in washing. Meaning is not the 1st item in the queue
void dequeue(int index); // same as the previous comment
Car getFront();
void getCarQueue(Queue);
int length();
Car get(int);
};
Queue::Queue() {
size = 0;
front = 0;
rear = Q_MAX_SIZE -1;
}
Queue::~Queue() {
while(!isEmpty()) {
dequeue();
}
}
void Queue::enqueue(Car c) {
if (!isFull()) {
rear = (rear + 1) % Q_MAX_SIZE; // circular array
carQueue[rear] = c;
size++;
} else {
cout << "Queue is currently full.\n";
}
}
void Queue::dequeue() {
}
void Queue::dequeue(int index) {
if(!isEmpty()) {
front = (front + 1) % Q_MAX_SIZE;
if(front != index) {
carQueue[index-1] = carQueue[index];
rear--;
size--;
} else {
cout << "Not allowed to dequeue the first car in the queue.\n";
}
} else {
cout << "There are no cars to dequeue.\n";
}
}
bool Queue::isEmpty() {
return size == 0;
}
bool Queue::isFull() {
return (size == Q_MAX_SIZE);
}
Car Queue::getFront() {
return carQueue[front];
}
int Queue::length() {
return size;
}
Car Queue::get(int index) {
return carQueue[index-1];
}
void Queue::getCarQueue(Queue q) {
for(int i = 0; i< q.length(); i++)
cout << q.get(i) << endl; // Error here
}
#endif
error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Car' (or there is no acceptable conversion)
I get this error which is kind of odd. so is there anything wrong? Thanks!
cout has no idea how to process a car object; it has never seen a car object and doesn't know how you output a car as text. cout can only process types it knows about, string, char, int, etc. The specific error is because there is version of operator << that takes an ostream and a car.
There are two options:
Creation an overload for operator<< that takes an ostream and a car. That will show cout how to output a car. This isn't usually done becuase there is usually more than one way your would want to display a car.
Write the output statement so that it manually prints out car properties like
cout << c.getMake() << " " << c.getModel()