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

Limiting characters in PlainTextEdit field line by line using QStringList::iterator or QMutableStringListIterator - Always crashes at iteration 268



  • Hello again! I come to you with yet another problem I can't seem to solve on my own. After hours of frustration I see no other way but to seek help from the internet once again.

    The objective: the user will type in a PlainTextEdit field that has a character limit, line by line. The first line has a character limit of 57 and all the rest have a limit of 69 characters. (This is not intentional. It's the maximum space that the text can occupy on an A4 sized document)

    I know I could just do a form with a bunch of LineEdits, set char limits for each of them and call it a day, but I'm not at that stage of giving up just yet.

    The approach: what I did was connect the text field to a textChanged signal and separate the content in a QStringList, line by line, each time the user types something in. Then, I created an iterator (tried with both java style and STL) to read through the lines, checking if any of them surpass the character limit. When a line has more than it can hold, the content of that line will overflow and go to the next line. If there is no other line, a new one is added with the append() function. Then, the program will keep searching for line overflows and finally do a join() and set the text of the TextEdit to the new QString.

    The problem: everything works just fine until the fourth line, when the character count in the text field is 268. Then, it crashes unexpectedly with no explanation whatsoever.

    The crash:

    16:22:38: The program has unexpectedly finished.
    16:22:38: The process was ended forcefully.
    16:22:38: C:/Users/Dante/Documents/build-PB-Desktop_Qt_5_12_0_MSVC2017_32bit-Debug/debug/PB.exe crashed.
    

    Sorry, this is all I could gather :(

    And without further ado,

    The code:

    With java style iterator:

    void MainWindow::on_input_plainTextEdit_textChanged()
    {
        ui->input_plainTextEdit->blockSignals(true);
        QPlainTextEdit * text = ui->input_plainTextEdit;
        QTextDocument * doc = text->document();
        QStringList linesText = text->toPlainText().split(QRegExp("[\n]"));
        QTextCursor textcursor = text->textCursor();
        
        int cursorPos = textcursor.position();
        int cursorPosLine = textcursor.positionInBlock();
        int charlim = 0;
        
        QStringList::iterator strlit;                       //iterators
        QMutableStringListIterator st(linesText);
        
        while(st.hasNext()) {
            if(!st.hasPrevious()) {     //first line has a 57 char limit
                charlim = 57;
            } else {                    //any other line has a 69 char limit
                charlim = 69;
            }
            st.next();                  //jump over to get value()
            if((st.value().length() > charlim) && ((st.hasNext()) || (linesText.size() < 18))) {
                if(!st.hasNext()) {
                    linesText.append("");                       //add a line
                    if(cursorPosLine > charlim) {               //correct cursor position
                        cursorPos++;
                    }
                }
                QString aux = st.value().mid(charlim);   //this aux will go to the next line (crashes here sometimes)
                st.value() = st.value().left(charlim);          //this stays in the current line
                st.next();                                      //jump over to next line
                st.value() = st.value().prepend(aux);           //PROGRAM WILL CRASH HERE WHEN ADDING 5TH LINE
                                                                //(268 CHARS LONG)
                st.previous();                                  //go back to the previous line
            }
        }
    
        text->setPlainText(linesText.join("\n"));
    
        ui->input_plainTextEdit->blockSignals(false);
        textcursor.setPosition(cursorPos);
        text->setTextCursor(textcursor);
    }
    

    I'm not including the STL approach since it does exactly the same, and crashes in the exact same place. Also I think this one is much cleaner.

    Anyways, I'm sure there's better ways to do this that I'm not seeing, so if anyone can point that out I'd be glad. Though I still would like to know why this is crashing!

    Thank you!



  • @dante77
    Can you not run this inside a debugger?


  • Qt Champions 2019

    Why that complicated? What happens with the overflowing text? Prepended to the next line? What happens with the \n then? Should a \n written by the user always be there after the fixup?

    btw:

    text->toPlainText().split(QRegExp("[\n]"));

    Why that complicated? Just you can said 'I've used a regex'?

    text->toPlainText().split(QLatin1Char('\n'));

    is much easier to read and faster



  • @JonB Thank you for answering!

    I did run this in a debugger and it gave me this:

    The inferior stopped because it triggered an exception.

    Stopped in thread 0 by: Exception at 0x7a674dbd, code: 0xc0000005: read access violation at: 0xfffffffffeeefef2, flags=0x0 (first chance).

    I still can't figure out what's causing this but I'm almost sure it's got something to do with the iterators.


  • Qt Champions 2017

    @dante77 said in Limiting characters in PlainTextEdit field line by line using QStringList::iterator or QMutableStringListIterator - Always crashes at iteration 268:

    The inferior stopped because it triggered an exception.

    Stopped in thread 0 by: Exception at 0x7a674dbd, code: 0xc0000005: read access violation at: 0xfffffffffeeefef2, flags=0x0 (first chance).

    That's simply a segfault (access violation in windows lingo), something's reading an invalid memory address is the most common cause.

    I still can't figure out what's causing this but I'm almost sure it's got something to do with the iterators.

    Every crash has a stack trace. Please provide one.



  • @kshegunov

    That's simply a segfault (access violation in windows lingo), something's reading an invalid memory address is the most common cause.

    After running the debugger a couple more times and understanding how it works (sorry), I noticed that my QStringList changes addresses after the append() function is called, but my QStringList iterator keeps pointing to the old addresses. Is this what's causing the segmentation fault? Is append() not safe to call while using an iterator on the same list?

    Every crash has a stack trace. Please provide one.

    I believe this is it.

    e1ef3c78-c492-4c27-8391-dbed2dc356b8-image.png


  • Qt Champions 2017

    @dante77 said in Limiting characters in PlainTextEdit field line by line using QStringList::iterator or QMutableStringListIterator - Always crashes at iteration 268:

    Is this what's causing the segmentation fault?

    Almost certainly.

    Is append() not safe to call while using an iterator on the same list?

    Nope. Append can (and often will) realloc to accommodate the new elements, so the old memory gets invalidated. Since you use a mutable iterator, can't you use that to append to the list?



  • @kshegunov

    Is append() not safe to call while using an iterator on the same list?

    Nope. Append can (and often will) realloc to accommodate the new elements, so the old memory gets invalidated. Since you use a mutable iterator, can't you use that to append to the list?

    Thank you so much! I wasn't sure that was possible. I guess I missed that in the documentation.

    For those who might have the same problem or are looking to do the same thing with a TextEdit field:

    instead of using append, I used QMutableStringListIterator::insert().