diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..f5d91e67 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,11 @@ +# This is a generated file do not edit but instead edit .clang-tidy.checks and .clang-tidy.settings + +Checks: '*,-abseil*,-altera-*,-android-*,-boost-*,-cppcoreguidelines-*,-darwin-*,-fuchsia-*,-hicpp-*,-llvmlibc-*,-linuxkernel-*,-mpi-*,-objc-*,-openmp-*,-zircon-*,-bugprone-unchecked-optional-access,-bugprone-narrowing-conversions,-bugprone-easily-swappable-parameters,-bugprone-branch-clone,-bugprone-implicit-widening-of-multiplication-result,-bugprone-suspicious-include,-bugprone-integer-division,-bugprone-suspicious-enum-usage,-cert-err58-cpp,-cert-dcl21-cpp,-cert-dcl50-cpp,-clang-analyzer-alpha*,-clang-analyzer-osx.*,-clang-analyzer-webkit.*,-clang-analyzer-cplusplus.NewDeleteLeaks,-clang-analyzer-core.CallAndMessage,-clang-analyzer-alpha.core.CastToStruct,-clang-analyzer-alpha.deadcode.UnreachableCode,-clang-analyzer-core.NonNullParamChecker,-concurrency-*,-clang-diagnostic-pragma-once-outside-header,-google-*,google-explicit-constructor,google-readability-namespace-comments,-llvm-include-order,-llvm-namespace-comment,-llvm-qualified-auto,-llvm-else-after-return,-llvm-header-guard,-misc-no-recursion,-misc-non-private-member-variables-in-classes,-misc-const-correctness,-misc-include-cleaner,-modernize-use-override,-modernize-use-bool-literals,-modernize-pass-by-value,-modernize-raw-string-literal,-modernize-use-using,-modernize-loop-convert,-modernize-return-braced-init-list,-modernize-use-equals-default,-modernize-use-trailing-return-type,-modernize-use-nodiscard,-modernize-avoid-bind,-modernize-avoid-c-arrays,-modernize-type-traits,-performance-unnecessary-copy-initialization,-performance-no-automatic-move,-performance-no-int-to-ptr,-performance-enum-size,-readability-else-after-return,-readability-named-parameter,-readability-implicit-bool-conversion,-readability-namespace-comments,-readability-qualified-auto,-readability-magic-numbers,-readability-braces-around-statements,-readability-function-cognitive-complexity,-readability-const-return-type,-readability-inconsistent-declaration-parameter-name,-readability-simplify-boolean-expr,-readability-redundant-access-specifiers,-readability-identifier-length,-readability-misleading-indentation,-readability-make-member-function-const,-readability-suspicious-call-argument,-readability-avoid-nested-conditional-operator,-clang-analyzer-security.insecureAPI.*,-performance-unnecessary-value-param,-readability-convert-member-functions-to-static,-bugprone-misplaced-widening-cast,' + +CheckOptions: + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: 1 + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted + value: 1 + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: 0 diff --git a/.clang-tidy.checks b/.clang-tidy.checks new file mode 100644 index 00000000..7790f092 --- /dev/null +++ b/.clang-tidy.checks @@ -0,0 +1,105 @@ +# I want all checks by default. That way I'll have to black list new checks rather than whitelist them +* + +-abseil* # Try +-altera-* +-android-* +-boost-* +-cppcoreguidelines-* # Try +-darwin-* +-fuchsia-* +-hicpp-* # Tryu +-llvmlibc-* +-linuxkernel-* +-mpi-* +-objc-* +-openmp-* +-zircon-* + +-bugprone-unchecked-optional-access # https://github.com/llvm/llvm-project/issues/55530 +-bugprone-narrowing-conversions +-bugprone-easily-swappable-parameters +-bugprone-branch-clone +-bugprone-implicit-widening-of-multiplication-result +-bugprone-suspicious-include # This one doesn't like us importing moc_XXX.cpp files +-bugprone-integer-division +-bugprone-suspicious-enum-usage # in Qt5 this complains about Qt::CTRL | Qt::Key_a which is the right way to do it in Qt6 + +-cert-err58-cpp +-cert-dcl21-cpp + +# I only use this when I ignore the parameters altogether (See ContactsPage.cpp e.g) +-cert-dcl50-cpp + +-clang-analyzer-alpha* +-clang-analyzer-osx.* +-clang-analyzer-webkit.* +-clang-analyzer-cplusplus.NewDeleteLeaks +-clang-analyzer-core.CallAndMessage +-clang-analyzer-alpha.core.CastToStruct +-clang-analyzer-alpha.deadcode.UnreachableCode +-clang-analyzer-core.NonNullParamChecker + +-concurrency-* + +# This gives false possitive in all header files. +-clang-diagnostic-pragma-once-outside-header + +-google-* # Try +google-explicit-constructor +google-readability-namespace-comments + +-llvm-include-order +-llvm-namespace-comment +-llvm-qualified-auto +-llvm-else-after-return +-llvm-header-guard # complains even though we have #pragma once + +-misc-no-recursion +-misc-non-private-member-variables-in-classes # I agree instance variables should be private, but often I do this by design - too noisy. +-misc-const-correctness # Lots of hits, lets ignore it for now. +-misc-include-cleaner # Completely broken as of Oct 2023 + +-modernize-use-override +-modernize-use-bool-literals +-modernize-pass-by-value +-modernize-raw-string-literal +-modernize-use-using +-modernize-loop-convert +-modernize-return-braced-init-list +-modernize-use-equals-default +-modernize-use-trailing-return-type +-modernize-use-nodiscard +-modernize-avoid-bind +-modernize-avoid-c-arrays +-modernize-type-traits # Lots of errors from QTestLib + +-performance-unnecessary-copy-initialization +-performance-no-automatic-move # often doesn't do much of a difference in Qt apps, due to their ref counting. +-performance-no-int-to-ptr +-performance-enum-size # int is faster than int8_t + +-readability-else-after-return +-readability-named-parameter +-readability-implicit-bool-conversion # Lots of warnings about if (myPointer) -> if (myPointer != nullptr) +-readability-namespace-comments +-readability-qualified-auto +-readability-magic-numbers +-readability-braces-around-statements +-readability-function-cognitive-complexity +-readability-const-return-type +-readability-inconsistent-declaration-parameter-name +-readability-simplify-boolean-expr +-readability-redundant-access-specifiers +-readability-identifier-length +-readability-misleading-indentation +-readability-make-member-function-const # complains about functions updating pointers too +-readability-suspicious-call-argument +-readability-avoid-nested-conditional-operator # I simply do not agree that this is hard to read, esp with clang-format formatting it nicely + +-clang-analyzer-security.insecureAPI.* + +# Try all the rest in this file +-performance-unnecessary-value-param +-readability-convert-member-functions-to-static +-bugprone-misplaced-widening-cast diff --git a/.clang-tidy.settings b/.clang-tidy.settings new file mode 100644 index 00000000..1c5262fe --- /dev/null +++ b/.clang-tidy.settings @@ -0,0 +1,7 @@ +CheckOptions: + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: 1 + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted + value: 1 + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: 0 diff --git a/QXlsx/header/xlsxdocument.h b/QXlsx/header/xlsxdocument.h index 492f1a6b..be68212f 100644 --- a/QXlsx/header/xlsxdocument.h +++ b/QXlsx/header/xlsxdocument.h @@ -130,7 +130,7 @@ class QXLSX_EXPORT Document : public QObject bool autosizeColumnWidth(const CellRange &range); bool autosizeColumnWidth(int column); bool autosizeColumnWidth(int colFirst, int colLast); - bool autosizeColumnWidth(void); + bool autosizeColumnWidth(); private: QMap getMaximalColumnWidth(int firstRow = 1, int lastRow = INT_MAX); diff --git a/QXlsx/header/xlsxworksheet.h b/QXlsx/header/xlsxworksheet.h index cdeb0f1b..ccf48564 100644 --- a/QXlsx/header/xlsxworksheet.h +++ b/QXlsx/header/xlsxworksheet.h @@ -186,7 +186,7 @@ class QXLSX_EXPORT Worksheet : public AbstractSheet void setWhiteSpaceVisible(bool visible); bool setStartPage(int spagen); // add by liufeijin20181028 - QVector getFullCells(int *maxRow, int *maxCol); + QVector getFullCells(int *maxRow, int *maxCol) const; private: void saveToXmlFile(QIODevice *device) const override; diff --git a/QXlsx/source/xlsxcell.cpp b/QXlsx/source/xlsxcell.cpp index 348400d7..2de4718b 100644 --- a/QXlsx/source/xlsxcell.cpp +++ b/QXlsx/source/xlsxcell.cpp @@ -84,8 +84,7 @@ Cell::Cell(const Cell *const cell) */ Cell::~Cell() { - if (nullptr != d_ptr) - delete d_ptr; + delete d_ptr; } /*! diff --git a/QXlsx/source/xlsxcellformula.cpp b/QXlsx/source/xlsxcellformula.cpp index a5a076bd..8be44ac5 100644 --- a/QXlsx/source/xlsxcellformula.cpp +++ b/QXlsx/source/xlsxcellformula.cpp @@ -104,7 +104,10 @@ CellFormula::CellFormula(const CellFormula &other) */ CellFormula &CellFormula::operator=(const CellFormula &other) { - d = other.d; + if (this != &other) // Self-assignment check [cert-oop54-cpp] + { + this->d = other.d; + } return *this; } diff --git a/QXlsx/source/xlsxchart.cpp b/QXlsx/source/xlsxchart.cpp index b5ae1599..7e522944 100644 --- a/QXlsx/source/xlsxchart.cpp +++ b/QXlsx/source/xlsxchart.cpp @@ -1168,25 +1168,6 @@ void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) writer.writeEndElement(); } -#if 0 - if (!ser->axDataSource_numRef.isEmpty()) - { - if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart) - { - writer.writeStartElement(QStringLiteral("c:xVal")); - } - else - { - writer.writeStartElement(QStringLiteral("c:cat")); - } - - writer.writeStartElement(QStringLiteral("c:numRef")); - writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef); - writer.writeEndElement();//c:numRef - writer.writeEndElement();//c:cat or c:xVal - } -#endif - if (!ser->numberDataSource_numRef.isEmpty()) { if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart) writer.writeStartElement(QStringLiteral("c:yVal")); diff --git a/QXlsx/source/xlsxchartsheet.cpp b/QXlsx/source/xlsxchartsheet.cpp index ea743d0e..0db168f2 100644 --- a/QXlsx/source/xlsxchartsheet.cpp +++ b/QXlsx/source/xlsxchartsheet.cpp @@ -42,8 +42,7 @@ Chartsheet::Chartsheet(const QString &name, int id, Workbook *workbook, CreateFl if (flag == Chartsheet::F_NewFromScratch) { d_func()->drawing = std::make_shared(this, flag); - DrawingAbsoluteAnchor *anchor = - new DrawingAbsoluteAnchor(drawing(), DrawingAnchor::Picture); + auto anchor = new DrawingAbsoluteAnchor(drawing(), DrawingAnchor::Picture); anchor->pos = QPoint(0, 0); anchor->ext = QSize(9293679, 6068786); diff --git a/QXlsx/source/xlsxconditionalformatting.cpp b/QXlsx/source/xlsxconditionalformatting.cpp index c30d8d41..f786a2d7 100644 --- a/QXlsx/source/xlsxconditionalformatting.cpp +++ b/QXlsx/source/xlsxconditionalformatting.cpp @@ -147,7 +147,10 @@ ConditionalFormatting::ConditionalFormatting(const ConditionalFormatting &other) */ ConditionalFormatting &ConditionalFormatting::operator=(const ConditionalFormatting &other) { - this->d = other.d; + if (this != &other) // Self-assignment check [cert-oop54-cpp] + { + this->d = other.d; + } return *this; } @@ -786,9 +789,11 @@ bool ConditionalFormatting::saveToXml(QXmlStreamWriter &writer) const writer.writeTextElement(QStringLiteral("formula"), it.value().toString().arg(startCell)); } - } else if ((it = rule->attrs.constFind(XlsxCfRuleData::A_formula1)) != - rule->attrs.constEnd()) { - writer.writeTextElement(QStringLiteral("formula"), it.value().toString()); + } else { + it = rule->attrs.constFind(XlsxCfRuleData::A_formula1); + if (it != rule->attrs.constEnd()) { + writer.writeTextElement(QStringLiteral("formula"), it.value().toString()); + } } it = rule->attrs.constFind(XlsxCfRuleData::A_formula2); diff --git a/QXlsx/source/xlsxdatavalidation.cpp b/QXlsx/source/xlsxdatavalidation.cpp index bf8fa512..09122d9d 100644 --- a/QXlsx/source/xlsxdatavalidation.cpp +++ b/QXlsx/source/xlsxdatavalidation.cpp @@ -137,7 +137,10 @@ DataValidation::DataValidation(const DataValidation &other) */ DataValidation &DataValidation::operator=(const DataValidation &other) { - this->d = other.d; + if (this != &other) // Self-assignment check [cert-oop54-cpp] + { + this->d = other.d; + } return *this; } diff --git a/QXlsx/source/xlsxdocument.cpp b/QXlsx/source/xlsxdocument.cpp index f5987f1a..5772a2d9 100644 --- a/QXlsx/source/xlsxdocument.cpp +++ b/QXlsx/source/xlsxdocument.cpp @@ -72,8 +72,8 @@ std::string copyTag(const std::string &sFrom, const std::string &sTo, const std: const std::string tagEnd = ""; // search all occurrences of tag in 'sFrom' - std::string sFromData = ""; - size_t startIndex = 0; + std::string sFromData; + size_t startIndex = 0; while (true) { std::size_t startPos = sFrom.find(tagToFindStart, startIndex); if (startPos != std::string::npos) { @@ -491,7 +491,7 @@ bool DocumentPrivate::saveCsv(QString mainCSVFileName) const currentSheet->workbook()->setActiveSheet(sheetIndexNumber); - Worksheet *wsheet = static_cast(currentSheet->workbook()->activeSheet()); + auto wsheet = static_cast(currentSheet->workbook()->activeSheet()); if (wsheet == nullptr) { continue; } @@ -572,7 +572,7 @@ bool DocumentPrivate::copyStyle(const QString &from, const QString &to) ZipReader zipReader(from); QStringList filePaths = zipReader.filePaths(); - std::shared_ptr toReader = std::shared_ptr(new ZipReader(to)); + auto toReader = std::make_shared(to); QStringList toFilePaths = toReader->filePaths(); @@ -1539,7 +1539,7 @@ bool Document::autosizeColumnWidth(int colFirst, int colLast) Auto sets width in characters for all columns. Returns true on success. */ -bool Document::autosizeColumnWidth(void) +bool Document::autosizeColumnWidth() { bool erg = false; diff --git a/QXlsx/source/xlsxnumformatparser.cpp b/QXlsx/source/xlsxnumformatparser.cpp index 48b2d49f..a307c452 100644 --- a/QXlsx/source/xlsxnumformatparser.cpp +++ b/QXlsx/source/xlsxnumformatparser.cpp @@ -29,8 +29,12 @@ bool NumFormatParser::isDateTime(const QString &formatCode) // quoted plain text block: don't care, ignore case '"': - while (i < formatCode.length() - 1 && formatCode[++i] != QLatin1Char('"')) - ; + while (i < formatCode.length() - 1) { + ++i; + if (formatCode[i] == QLatin1Char('"')) { + break; + } + } break; // escaped char: don't care, ignore diff --git a/QXlsx/source/xlsxstyles.cpp b/QXlsx/source/xlsxstyles.cpp index e4247f7a..2db27387 100644 --- a/QXlsx/source/xlsxstyles.cpp +++ b/QXlsx/source/xlsxstyles.cpp @@ -148,24 +148,26 @@ void Styles::fixNumFmt(const Format &format) const auto &str = format.numberFormat(); if (!str.isEmpty()) { - QHash>::ConstIterator cIt; // Assign proper number format index const auto &it = m_builtinNumFmtsHash.constFind(str); if (it != m_builtinNumFmtsHash.constEnd()) { const_cast(&format)->fixNumberFormat(it.value(), str); - } else if ((cIt = m_customNumFmtsHash.constFind(str)) != m_customNumFmtsHash.constEnd()) { - const_cast(&format)->fixNumberFormat(cIt.value()->formatIndex, str); } else { - // Assign a new fmt Id. - const_cast(&format)->fixNumberFormat(m_nextCustomNumFmtId, str); - - auto fmt = std::make_shared(); - fmt->formatIndex = m_nextCustomNumFmtId; - fmt->formatString = str; - m_customNumFmtIdMap.insert(m_nextCustomNumFmtId, fmt); - m_customNumFmtsHash.insert(str, fmt); - - m_nextCustomNumFmtId += 1; + auto cIt = m_customNumFmtsHash.constFind(str); + if (cIt != m_customNumFmtsHash.constEnd()) { + const_cast(&format)->fixNumberFormat(cIt.value()->formatIndex, str); + } else { + // Assign a new fmt Id. + const_cast(&format)->fixNumberFormat(m_nextCustomNumFmtId, str); + + auto fmt = std::make_shared(); + fmt->formatIndex = m_nextCustomNumFmtId; + fmt->formatString = str; + m_customNumFmtIdMap.insert(m_nextCustomNumFmtId, fmt); + m_customNumFmtsHash.insert(str, fmt); + + m_nextCustomNumFmtId += 1; + } } } else { const auto id = format.numberFormatIndex(); @@ -350,7 +352,7 @@ void Styles::saveToXmlFile(QIODevice *device) const void Styles::writeNumFmts(QXmlStreamWriter &writer) const { - if (m_customNumFmtIdMap.size() == 0) + if (m_customNumFmtIdMap.isEmpty()) return; writer.writeStartElement(QStringLiteral("numFmts")); @@ -433,7 +435,7 @@ void Styles::writeFont(QXmlStreamWriter &writer, const Format &format, bool isDx } if (format.hasProperty(FormatPrivate::P_Font_Color)) { - XlsxColor color = format.property(FormatPrivate::P_Font_Color).value(); + auto color = format.property(FormatPrivate::P_Font_Color).value(); color.saveToXml(writer); } diff --git a/QXlsx/source/xlsxutility.cpp b/QXlsx/source/xlsxutility.cpp index d2dd6362..30c9d4d7 100644 --- a/QXlsx/source/xlsxutility.cpp +++ b/QXlsx/source/xlsxutility.cpp @@ -90,7 +90,7 @@ QVariant datetimeFromNumber(double num, bool is1904) num = num - 1; } - qint64 msecs = static_cast(num * 1000 * 60 * 60 * 24.0 + 0.5); + auto msecs = static_cast(std::lround(num * 1000 * 60 * 60 * 24.0)); if (is1904) msecs += msecs1904; diff --git a/QXlsx/source/xlsxworksheet.cpp b/QXlsx/source/xlsxworksheet.cpp index b3b2e8ba..9f692f94 100644 --- a/QXlsx/source/xlsxworksheet.cpp +++ b/QXlsx/source/xlsxworksheet.cpp @@ -187,7 +187,7 @@ Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag Worksheet *Worksheet::copy(const QString &distName, int distId) const { Q_D(const Worksheet); - Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch); + auto sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch); WorksheetPrivate *sheet_d = sheet->d_func(); sheet_d->dimension = d->dimension; @@ -1037,8 +1037,8 @@ bool Worksheet::writeHyperlink(int row, d->cellTable.setValue(row, column, cell); // Store the hyperlink data in a separate table - d->urlTable[row][column] = std::shared_ptr(new XlsxHyperlinkData( - XlsxHyperlinkData::External, urlString, locationString, QString(), tip)); + d->urlTable[row][column] = std::make_shared( + XlsxHyperlinkData::External, urlString, locationString, QString{}, tip); return true; } @@ -1094,16 +1094,15 @@ int Worksheet::insertImage(int row, int column, const QImage &image) d->drawing = std::make_shared(this, F_NewFromScratch); } - DrawingOneCellAnchor *anchor = - new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture); + auto anchor = new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture); /* The size are expressed as English Metric Units (EMUs). EMU is 1/360 000 of centimiter. */ anchor->from = XlsxMarker(row, column, 0, 0); - float scaleX = 36e6f / std::max(1, image.dotsPerMeterX()); - float scaleY = 36e6f / std::max(1, image.dotsPerMeterY()); + float scaleX = 36e6F / std::max(1, image.dotsPerMeterX()); + float scaleY = 36e6F / std::max(1, image.dotsPerMeterY()); anchor->ext = QSize(int(image.width() * scaleX), int(image.height() * scaleY)); anchor->setObjectPicture(image); @@ -1184,8 +1183,7 @@ Chart *Worksheet::insertChart(int row, int column, const QSize &size) if (!d->drawing) d->drawing = std::make_shared(this, F_NewFromScratch); - DrawingOneCellAnchor *anchor = - new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture); + auto anchor = new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture); /* The size are expressed as English Metric Units (EMUs). There are @@ -1951,12 +1949,6 @@ double Worksheet::columnWidth(int column) QList> columnInfoList = d->getColumnInfoList(column, column); - // [dev54] - if (columnInfoList.size() == 0) { - // column information is not found - // qDebug() << "[debug]" << __FUNCTION__ << "column (info) is not found. " << column; - } - if (columnInfoList.count() == 1) { // column information is found // qDebug() << "[debug]" << __FUNCTION__ << "column (info) is found. " << column << @@ -2114,7 +2106,7 @@ bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed) if (it != d->rowsInfo.end()) { (*it)->outlineLevel += 1; } else { - std::shared_ptr info(new XlsxRowInfo); + auto info = std::make_shared(); info->outlineLevel += 1; it = d->rowsInfo.insert(row, info); } @@ -2124,7 +2116,7 @@ bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed) if (collapsed) { auto it = d->rowsInfo.find(rowLast + 1); if (it == d->rowsInfo.end()) - it = d->rowsInfo.insert(rowLast + 1, std::shared_ptr(new XlsxRowInfo)); + it = d->rowsInfo.insert(rowLast + 1, std::make_shared()); (*it)->collapsed = true; } return true; @@ -2243,9 +2235,9 @@ int WorksheetPrivate::colPixelsSize(int col) const if (it != col_sizes.constEnd()) { double width = it.value(); if (width < 1) - pixels = static_cast(width * (max_digit_width + padding) + 0.5); + pixels = static_cast(std::lround(width * (max_digit_width + padding))); else - pixels = static_cast(width * max_digit_width + 0.5) + padding; + pixels = static_cast(std::lround(width * max_digit_width)) + padding; } else { pixels = 64; } @@ -2833,7 +2825,7 @@ SharedStrings *WorksheetPrivate::sharedStrings() const return workbook->sharedStrings(); } -QVector Worksheet::getFullCells(int *maxRow, int *maxCol) +QVector Worksheet::getFullCells(int *maxRow, int *maxCol) const { Q_D(const Worksheet); @@ -2855,10 +2847,11 @@ QVector Worksheet::getFullCells(int *maxRow, int *maxCol) return ret; } - for (const auto row : d->cellTable.sortedRows()) { - auto &columns = d->cellTable.cells[row]; - auto columnsSorted = CellTable::sorteIntList(columns.keys()); - for (const auto col : columnsSorted) { + const auto sortedRows = d->cellTable.sortedRows(); + for (const auto row : sortedRows) { + const auto &columns = d->cellTable.cells[row]; + const auto columnsSorted = CellTable::sorteIntList(columns.keys()); + for (const auto &col : columnsSorted) { // It's faster to iterate but cellTable is unordered which might not // be what callers want? auto cell = std::make_shared(columns.value(col).get());