Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Problem with QHash container (can't get a key in some condition)



  • Hello everyone,
    It's my first post here about my first application in Qt, so please be understanding ;)

    I have container from my class Cart in MainWindow: QHash<Product, int>. One of pushButton create new Dialog, where it's a spinBox. Qty from the spinBox is send back to MainWindow. I insert this number from Dialog with Product object to QHash container. Later if I want to get a value there is no problem. Also everything is ok when I want to get a key, if qty is less than 10. If value (qty from spinBox) is 10 or greater and I want to get a key I have a segmentation fault :/

    I can't find any clue in debbuger or here or stackoverflow. Do you have any idea, what can it be? If my description it's not enough I can send a code. Thanks for help.


  • Qt Champions 2019

    @Krrris88 said in Problem with QHash container (can't get a key in some condition):

    want to get a key I have a segmentation fault :/

    Take a look at the backtrace to see where it happens. Try to create a minimal example to reproduce the issue. Without code we can just guess.



  • Ok, so below I send code:

    Product.hpp

    #pragma once
    
    #include <QString>
    
    class Product
    {
    public:
    
        Product(QString newName = "NULL", int newPrice = 0, int newTax = 0)
            : name(newName), netto(newPrice), VAT(newTax) {}
    
        int getNetto() const;
        int getVAT() const;
        QString getName() const;
        double getPriceWithTax() const;
    
        bool operator == (const Product &newObject) const;
    
    private:
    
        const int netto, VAT; // PRICE IN PLN SUBUNIT - GROSZ
        const QString name;
    };
    
    inline uint qHash(const Product &key, uint seed)
    {
        return qHash(key.getName(), seed) ^ key.getNetto();
    }
    

    Product.cpp

    #include "Product.hpp"
    
    int Product::getNetto() const
    {
        return netto;
    }
    
    int Product::getVAT() const
    {
        return VAT;
    }
    
    QString Product::getName() const
    {
        return name;
    }
    
    double Product::getPriceWithTax() const
    {
        return netto * (1 + VAT / 100.0) / 100.0;
    }
    
    bool Product::operator == (const Product &newObject) const
    {
        return name == newObject.name;
    }
    

    Cart.hpp

    #pragma once
    
    #include <QHash>
    #include "Product.hpp"
    
    class Cart
    {
    public:
    
        QHash<Product, int> getCartQHashContainer();
        void addProduct(const Product newProduct, const int newQty);
    
    private:
    
        QHash<Product, int> cartQHashContainer;
        static const int cartCapacity = 10;
        static const int maxQtyOfOneProductInCart = 25;
    };
    

    Cart.cpp

    #include "Cart.hpp"
    #include <QDebug>
    
    QHash<Product, int> Cart::getCartQHashContainer()
    {
        return cartQHashContainer;
    }
    
    void Cart::addProduct(const Product newProduct, const int newQty)
    {
    //    auto it = cartQHashContainer.find(newProduct);
        QHash<Product, int>::iterator it = cartQHashContainer.find(newProduct);
    
        if (it != cartQHashContainer.end())
        {
            if (it.value() + newQty > maxQtyOfOneProductInCart)
                qDebug() << "Nie zmiesci sie tyle w koszyku.\n"; // emit
            else
            {
                it.value() += newQty;
                qDebug() << "Dodales do koszyka " << newProduct.getName() << ", w ilosci: " << newQty << '\n'; // emit
            }
    
            return;
        }
    
        if (cartQHashContainer.size() >= cartCapacity)
        {
            qDebug() << "W koszyku jest juz 10 roznych produktow. Aby dodac kolejny usun przynajmniej jeden produkt z koszyka.\n"; // emit
            return;
        }
    
        if (newQty > maxQtyOfOneProductInCart)
            qDebug() << "Nie zmiesci sie tyle w koszyku.\n"; // emit
        else
        {
            cartQHashContainer.insert(newProduct, newQty);
            qDebug() << "Dodales do PUSTEGO koszyka " << newProduct.getName() << ", w ilosci: " << newQty << '\n'; // emit
        }
    }
    

    addproductdialog.hpp

    #ifndef ADDPRODUCTDIALOG_HPP
    #define ADDPRODUCTDIALOG_HPP
    
    #include <QDialog>
    #include <QSpinBox>
    
    namespace Ui {
    class AddProductDialog;
    }
    
    class AddProductDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        explicit AddProductDialog(QWidget *parent = 0);
        ~AddProductDialog();
    
    signals:
        void sendQtyOfProductToMainWindow(int valueFromSpinBoxAddProduct);
    
    private slots:
        void on_buttonBox_accepted();
    
    private:
        Ui::AddProductDialog *ui;
    };
    
    #endif // ADDPRODUCTDIALOG_HPP
    

    addproductdialog.cpp

    #include "addproductdialog.hpp"
    #include "ui_addproductdialog.h"
    
    AddProductDialog::AddProductDialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::AddProductDialog)
    {
        ui->setupUi(this);
    }
    
    AddProductDialog::~AddProductDialog()
    {
        delete ui;
    }
    
    void AddProductDialog::on_buttonBox_accepted()
    {
        emit sendQtyOfProductToMainWindow(ui->spinBoxAddProduct->value());
    }
    

    mainwindow.hpp

    #ifndef MAINWINDOW_HPP
    #define MAINWINDOW_HPP
    
    #include <QMainWindow>
    #include <QPushButton>
    #include <QString>
    #include <array>
    #include "Product.hpp"
    #include "Cart.hpp"
    #include <QDebug>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private slots:
        void on_pushButtonHome_clicked();
        void on_pushButtonShowArticles_clicked();
        void on_pushButtonAddProduct_clicked();
        void on_pushButtonRemoveProduct_clicked();
        void on_pushButtonShowCart_clicked();
        void on_pushButtonLoadCart_clicked();
        void on_pushButtonPay_clicked();
    
        void addGooseberryToCart(int valueFromSpinBoxAddProduct);
        void addBananaToCart(int valueFromSpinBoxAddProduct);
    
        void on_pushButtonGooseberry_clicked();
        void on_pushButtonBanana_clicked();
    
    private:
        Ui::MainWindow *ui;
        Cart shopCart;
        static const int qtyOfProductsInShop = 15;
        std::array<Product, qtyOfProductsInShop> productsInShop;
    };
    
    #endif // MAINWINDOW_HPP
    

    mainwindow.cpp

    #include "mainwindow.hpp"
    #include "ui_mainwindow.h"
    #include "addproductdialog.hpp"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow),
        productsInShop({Product("Agrest", 1000, 23), Product("Banan", 2000, 23), Product("Borówka", 3000, 23),
                       Product("Gruszka", 600, 23), Product("Jabłko", 500, 23), Product("Pożeczka", 450, 23),
                       Product("Truskawka", 1500, 23), Product("Cebula", 50, 8), Product("Marchewka", 150, 8),
                       Product("Ogórek", 500, 8), Product("Papryka", 900, 8), Product("Pieczarka", 350, 8),
                       Product("Pomidor", 95, 8), Product("Rzodkiewka", 200, 8), Product("Reklamówka", 20, 90)})
    {
        ui->setupUi(this);
    
        ui->stackedWidget->setCurrentIndex(0);
    
        ui->labelProductName_0->setText((productsInShop.at(0)).getName());
        ui->labelProductName_1->setText((productsInShop.at(1)).getName());
        ui->labelProductName_2->setText((productsInShop.at(2)).getName());
        ui->labelProductName_3->setText((productsInShop.at(3)).getName());
        ui->labelProductName_4->setText((productsInShop.at(4)).getName());
        ui->labelProductName_5->setText((productsInShop.at(5)).getName());
        ui->labelProductName_6->setText((productsInShop.at(6)).getName());
        ui->labelProductName_7->setText((productsInShop.at(7)).getName());
        ui->labelProductName_8->setText((productsInShop.at(8)).getName());
        ui->labelProductName_9->setText((productsInShop.at(9)).getName());
        ui->labelProductName_10->setText((productsInShop.at(10)).getName());
        ui->labelProductName_11->setText((productsInShop.at(11)).getName());
        ui->labelProductName_12->setText((productsInShop.at(12)).getName());
        ui->labelProductName_13->setText((productsInShop.at(13)).getName());
        ui->labelProductName_14->setText((productsInShop.at(14)).getName());
    
        ui->labelProductPrice_0->setText(QString::number((productsInShop.at(0)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_1->setText(QString::number((productsInShop.at(1)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_2->setText(QString::number((productsInShop.at(2)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_3->setText(QString::number((productsInShop.at(3)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_4->setText(QString::number((productsInShop.at(4)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_5->setText(QString::number((productsInShop.at(5)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_6->setText(QString::number((productsInShop.at(6)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_7->setText(QString::number((productsInShop.at(7)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_8->setText(QString::number((productsInShop.at(8)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_9->setText(QString::number((productsInShop.at(9)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_10->setText(QString::number((productsInShop.at(10)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_11->setText(QString::number((productsInShop.at(11)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_12->setText(QString::number((productsInShop.at(12)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_13->setText(QString::number((productsInShop.at(13)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductPrice_14->setText(QString::number((productsInShop.at(14)).getPriceWithTax(), 'f', 2) + " PLN");
    
        ui->labelProductNamePrice_0->setText((productsInShop.at(0)).getName() + '\n' + QString::number((productsInShop.at(0)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_1->setText((productsInShop.at(1)).getName() + '\n' + QString::number((productsInShop.at(1)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_2->setText((productsInShop.at(2)).getName() + '\n' + QString::number((productsInShop.at(2)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_3->setText((productsInShop.at(3)).getName() + '\n' + QString::number((productsInShop.at(3)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_4->setText((productsInShop.at(4)).getName() + '\n' + QString::number((productsInShop.at(4)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_5->setText((productsInShop.at(5)).getName() + '\n' + QString::number((productsInShop.at(5)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_6->setText((productsInShop.at(6)).getName() + '\n' + QString::number((productsInShop.at(6)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_7->setText((productsInShop.at(7)).getName() + '\n' + QString::number((productsInShop.at(7)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_8->setText((productsInShop.at(8)).getName() + '\n' + QString::number((productsInShop.at(8)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_9->setText((productsInShop.at(9)).getName() + '\n' + QString::number((productsInShop.at(9)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_10->setText((productsInShop.at(10)).getName() + '\n' + QString::number((productsInShop.at(10)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_11->setText((productsInShop.at(11)).getName() + '\n' + QString::number((productsInShop.at(11)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_12->setText((productsInShop.at(12)).getName() + '\n' + QString::number((productsInShop.at(12)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_13->setText((productsInShop.at(13)).getName() + '\n' + QString::number((productsInShop.at(13)).getPriceWithTax(), 'f', 2) + " PLN");
        ui->labelProductNamePrice_14->setText((productsInShop.at(14)).getName() + '\n' + QString::number((productsInShop.at(14)).getPriceWithTax(), 'f', 2) + " PLN");
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButtonHome_clicked()
    {
        ui->stackedWidget->setCurrentIndex(0);
    }
    
    void MainWindow::on_pushButtonShowArticles_clicked()
    {
        ui->stackedWidget->setCurrentIndex(1);
    }
    
    void MainWindow::on_pushButtonAddProduct_clicked()
    {
        ui->stackedWidget->setCurrentIndex(2);
    }
    
    void MainWindow::on_pushButtonRemoveProduct_clicked()
    {
        ui->stackedWidget->setCurrentIndex(3);
    }
    
    void MainWindow::on_pushButtonShowCart_clicked()
    {
        ui->stackedWidget->setCurrentIndex(4);
    }
    
    void MainWindow::on_pushButtonLoadCart_clicked()
    {
        ui->stackedWidget->setCurrentIndex(5);
    }
    
    void MainWindow::on_pushButtonPay_clicked()
    {
        ui->stackedWidget->setCurrentIndex(6);
    }
    
    void MainWindow::addGooseberryToCart(int valueFromSpinBoxAddProduct)
    {
        shopCart.addProduct(productsInShop.at(0), valueFromSpinBoxAddProduct);
        ui->labelTest->setText(QString::number(valueFromSpinBoxAddProduct));
    
    //    auto it = (shopCart.getCartQHashContainer()).find(productsInShop.at(0));
        QHash<Product, int>::iterator it = (shopCart.getCartQHashContainer()).find(productsInShop.at(0));
        ui->labelTest2->setText(QString::number(it.value()));
    //    ui->labelTest3->setText((it.key()).getName());
        qDebug() << (it.key()).getName();
        qDebug() << "Size = " << (shopCart.getCartQHashContainer()).size();
        qDebug() << "Ilość = " << QString::number(it.value());
    }
    
    void MainWindow::addBananaToCart(int valueFromSpinBoxAddProduct)
    {
        ui->labelTest2->setText(QString::number(valueFromSpinBoxAddProduct));
    }
    
    void MainWindow::on_pushButtonGooseberry_clicked()
    {
        AddProductDialog addDialog;
    
        connect(&addDialog, SIGNAL(sendQtyOfProductToMainWindow(int)), this, SLOT(addGooseberryToCart(int)));
    
        addDialog.setModal(true);
        addDialog.exec();
    }
    
    void MainWindow::on_pushButtonBanana_clicked()
    {
        AddProductDialog addDialog;
    
        connect(&addDialog, SIGNAL(sendQtyOfProductToMainWindow(int)), this, SLOT(addBananaToCart(int)));
    
        addDialog.setModal(true);
        addDialog.exec();
    }
    

  • Qt Champions 2019

    Where does it crash? What's the backtrace?



  • It crashes in void MainWindow::addGooseberryToCart(int valueFromSpinBoxAddProduct). Exactly in line:

    qDebug() << (it.key()).getName();
    

    or this one:

    //    ui->labelTest3->setText((it.key()).getName());
    

    In debugger I follow variables. Everything is fine with key until program reach one of these lines mention above. When it happened in key are some trash.

    Sorry, but I'm not sure what do you mean about backtrace (I've never used it). Do you want what is show here:
    How to view Backtrace / Stacktrace?



  • @Krrris88 said in Problem with QHash container (can't get a key in some condition):

    qDebug() << (it.key()).getName();

    Are you sure that it has a valid state?
    Can you show how it is set?



  • @KroMignon

    QHash<Product, int>::iterator it = (shopCart.getCartQHashContainer()).find(productsInShop.at(0));
    


  • @Krrris88 And did you check if it is valid before call qDebug(), something like this:

    auto hash = shopCart.getCartQHashContainer();
    auto it = hash.find(productsInShop.at(0));
    while (it != hash.end())
    {
        qDebug() << (it.key()).getName();
        ++it;
    }
    


  • @Krrris88
    As @KroMignon has said.
    https://doc.qt.io/qt-5/qhash.html#find says:

    If the hash contains no item with the key, the function returns end().

    In which case, if it == end(), how does your (it.key()).getName() behave?



  • @KroMignon I tried your code and it works. Also I implement it to my code:

    auto it = (shopCart.getCartQHashContainer()).find(productsInShop.at(0));
    if(it != (shopCart.getCartQHashContainer()).end())
        ui->labelTest3->setText((it.key()).getName());
    

    And it also works :)

    But know it's question: WHY? Sorry but I have no idea how it works. Why one if-statement change it? There is still the SAME iterator.
    Why something like this can't work:

    auto it = (shopCart.getCartQHashContainer()).find(productsInShop.at(0));
    ui->labelTest3->setText((it.key()).getName());
    

    ??



  • @Krrris88 said in Problem with QHash container (can't get a key in some condition):

    Why something like this can't work:

    Because, when calling find() from QHash, you are not sure it will give you non empty result (iterator is at the end of the container, which is an invalid/null item).
    So you have to check if the result is valid.

    I would suggest you to take time to read documentation of QHash and QHash iterator



  • @KroMignon I know it. I know that find() return end(), if in container is no object with proper key and that is invalid.

    But tell me, if I have iterator:

    auto it = (shopCart.getCartQHashContainer()).find(productsInShop.at(0));
    

    Does this if-statement change the iterator or what? Because in both case the way of set the iterator is the same. In both situations iterator point to the same thing (proper thing, it's not end()), so why without if-statement it doesn't work? Hope you understand my point.



  • @Krrris88 said in Problem with QHash container (can't get a key in some condition):

    Does this if-statement change the iterator or what?

    No, this if statement do not change the iterator state, I think there are situations for which the find() does return an empty iterator. You can find it out with this code:

    auto hash=shopCart.getCartQHashContainer();
    auto it = hash.find(productsInShop.at(0));
    if(it != hash.end())
        ui->labelTest3->setText((it.key()).getName());
    else
       ui->labelTest3->setText("EMPTY ITERATOR!");
    


  • @Krrris88 said in Problem with QHash container (can't get a key in some condition):

    Does this if-statement change the iterator or what?

    I reply again, because there is something other I have not take care about!

    When you call getCartQHashContainer(), it will, I think returns you a copy of the QHash, depending on signature of this fonction!
    So the find will be done of a copy of the QMap.
    Then you call again getCartQHashContainer() to get end(), which will be another item..

    For me, getCartQHashContainer() should return a reference of the QMap:

    QHash<Product, int> & getCartQHashContainer();
    

    or with const, if you don't want to change map outside the class

    QHash<Product, int> & getCartQHashContainer() const;
    

    Is this the case?



  • @KroMignon I do not think that it's the problem with copy/reference. Look that I take twice container, but it's the same copy. Nothing change. First getter take copy for iterator and second for end(). But between these two lines nothing change container. So I think it only takes more memory and time for copy instead works on original container. Nevertheless I'll try it. Also I'll check your earlier idea (if/else) and let you know about results.



  • @Krrris88 said in Problem with QHash container (can't get a key in some condition):

    I do not think that it's the problem with copy/reference.

    But I do, look at this:

    QHash<int, QString> hash1;
    hash1[0] = "AAA";
    hash1[1] = "BB";
    hash1[2] = "CCC";
    hash1[3] = "DDD";
    hash1[4] = "EEE";
    
    QHash<int, QString> hash2(hash1);
    
    qDebug() << (hash1.end() == hash2.end() ? "OK" : "FAILURE");
    

    When executing this, you will obtain FAILURE.
    Try it out, if you don't believe me.



  • Yes, you're right with this end(). I changed getter and now it returns reference. It looks program works. Hope it's enough and there will be no more errors. Thanks for help.



  • @Krrris88 your welcome and don't forget the mark this topic as solved


Log in to reply