C++ 在所有模型索引及其子级上循环会导致堆栈溢出错误

C++ 在所有模型索引及其子级上循环会导致堆栈溢出错误,c++,qt,recursion,qabstractitemmodel,qmodelindex,C++,Qt,Recursion,Qabstractitemmodel,Qmodelindex,我创建了以下函数来循环表或树中的所有索引,并查找包含所需字符串的索引: #include <QModelIndex> #include <QAbstractItemModel> #include <QAbstractItemView> QModelIndex findIndexByString(const QAbstractItemModel* model, const QString& text, const QModelIndex& p

我创建了以下函数来循环表或树中的所有索引,并查找包含所需字符串的索引:

#include <QModelIndex>
#include <QAbstractItemModel>
#include <QAbstractItemView>

QModelIndex findIndexByString(const QAbstractItemModel* model, const QString& text,  const QModelIndex& parent = QModelIndex());
QModelIndex findIndexByString(const QModelIndex& index, const QString& text) {
    // First check this particular index
    if(index.data().toString() == text)
        return index;
    // Check if it has children and loop
    const QAbstractItemModel* model = index.model();
    if(model==nullptr)
        return QModelIndex();
    if (!model->hasChildren(index))
        return QModelIndex();
    // STACK OVERFLOW HERE (model and index keep repeating)
    return findIndexByString(model, text, index);
}
QModelIndex findIndexByString(const QAbstractItemModel* model, const QString& text,  const QModelIndex& parent) {
    int rows = model->rowCount(parent);
    int cols = model->columnCount(parent);
    for (int i = 0; i < rows; ++i)
      for (int j = 0; j < cols; ++j) {
        QModelIndex index_child = findIndexByString(model->index(i, j, parent), text);
        if(index_child.isValid())
            return index_child;
      }
    return QModelIndex();
}
QModelIndex findIndexByString(const QAbstractItemView* view, const QString& text) {
    //const QModelIndex root = view->rootIndex();
    const QAbstractItemModel* model = view->model();
    return findIndexByString(model, text);
}
#包括
#包括
#包括
QModelIndex findIndexByString(const qabstracttemmodel*model,const QString&text,const QModelIndex&parent=QModelIndex());
QModelIndex findIndexByString(常量QModelIndex&index,常量QString&text){
//首先检查这个特定的索引
if(index.data().toString()=文本)
收益指数;
//检查它是否有子项和循环
const qabstractemmodel*model=index.model();
如果(模型==nullptr)
返回QModelIndex();
如果(!model->hasChildren(索引))
返回QModelIndex();
//此处堆栈溢出(模型和索引不断重复)
返回findIndexByString(模型、文本、索引);
}
QModelIndex findIndexByString(const qabstractemmodel*model、const QString和text、const QModelIndex和parent){
int rows=model->rowCount(父级);
int cols=model->columnCount(父级);
对于(int i=0;iindex(i,j,parent),text);
if(index_child.isValid())
返回索引_子项;
}
返回QModelIndex();
}
QModelIndex findIndexByString(常量QAbstractItemView*视图、常量QString和文本){
//const QModelIndex root=view->rootIndex();
const qabstractemmodel*model=view->model();
返回findIndexByString(模型,文本);
}
此代码是自包含的,在Qt库之外没有任何依赖项

我在外文代码中的某个模型上循环时遇到堆栈溢出错误


我正在尝试创建通用的正确算法来实现这一点。上面的算法正确吗?

代码看起来不错。这是一个经典的深度优先搜索由于您的模型不是树,而是循环图和/或具有无限深度,因此它将耗尽堆栈。

我们可以使用显式堆栈重新格式化搜索,并添加测试,以确保在面对格式错误的模型时有合理的行为

如果您的模型具有无限的深度或设计周期,那么下面的代码将正常运行,并且不会耗尽堆栈

// https://github.com/KubaO/stackoverflown/tree/master/questions/model-find-diagnostic-44416637
#include <QtGui>
查找器的状态,我们将保持在显式堆栈上:

struct FindState {
   QModelIndex index;
   int rows;
   int cols;
   int i = 0;
   int j = 0;
   FindState() = default;
   FindState(const QAbstractItemModel* model, const QModelIndex & index) :
      index(index),
      rows(model->rowCount(index)),
      cols(model->columnCount(index)) {}
   bool isInitial() const { return i == 0 && j == 0; }
   bool equalTo(const QVariant & value) const {
      return index.isValid() && index.data() == value;
   }
   bool operator==(const FindState & o) const {
      return index == o.index;
   }
};

QDebug operator<<(QDebug dbg, const FindState & s) {
   return dbg << "{" << s.index << "," << s.i << "," << s.j << "}";
}
最后,我们可以创建一个模型,并确保两种查找项目的方法提供相同的结果:

int main() {
   QStandardItemModel model;
   Builder b(&model);
   Path path, ____;
   path = b.add({}, {1, 0});     // *(1,0)
   ____ = b.add(path, {1, 1});   //  (1,0)-(1,1)
   ____ = b.add(path, {0, 0});   //  (1,0)-(0,0)
   path = b.add({}, {1, 1});     // *(1,1)
   path = b.add(path, {3, 3});   // *(1,1)-(3,3)
   ____ = b.add(path, {0, 0});   //  (1,1)-(3,3)-(0,0)
   path.pop_back();              // -(1,1)
   path = b.add(path, {2, 2});   // *(1,1)-(2,2)
   ____ = b.add(path, {0, 1});   //  (1,1)-(2,2)-(0,1)
   path = b.add({}, {2, 1});     // *(2,1)

   IndexSet indexes;
   for (auto path : b.paths) {
      auto index = findIndexByString(b.model, path);
      auto index2 = findIndexByString2(b.model, path);
      Q_ASSERT(index.isValid());
      Q_ASSERT(!indexes.contains(index));
      Q_ASSERT(index2 == index);
      indexes.insert(index);
   }
}

示例到此结束。

您的模型有多深?当您出现溢出时,调用堆栈有多深?我猜递归可能没有正确终止。也可能模型被破坏了?如果自定义数据模型错误地实现了
索引
和/或
父项
,则可能会在模型中创建循环。这里的问题是我不了解导致错误的模型,因此我需要反馈算法是否正确。虽然模型不太可能被破坏,但是如果算法失败的唯一途径是一个破坏的模型,那么它可能真的被破坏了。非常感谢所有的努力!
QModelIndex findIndexByValue(const QAbstractItemModel* model, const QVariant& needle,
                             const QModelIndex& parent = {}) {
   int iterations = {};
   int maxDepth = {};
   const auto depthLimit = 100;
   IndexSet indexes;
   QStack<FindState> states;
   states.push({model, parent});
   for (; !states.isEmpty(); iterations++) {
      auto valid = true;
      auto & top = states.top();
      if (top.isInitial()) {
         if (states.size() > 1) {
            if (!top.index.isValid()) {
               qWarning() << "the index isn't valid, stack:" << states;
               valid = false;
            }
            if (!model->hasIndex(top.index.row(), top.index.column(), top.index.parent())) {
               qWarning() << "the index doesn't exist, stack:" << states;
               valid = false;
            }
         }
         if (indexes.contains(top.index)) {
            qWarning() << "skipping already seen index" << top.index;
            valid = false;
         }
         if (valid) {
            indexes.insert(top.index);
            if (top.equalTo(needle))
               break;
         }
      }
      if (valid && model->hasChildren(top.index) && top.i < top.rows && top.j < top.cols) {
         FindState state(model, model->index(top.i, top.j, top.index));
         top.i ++;
         if (top.i == top.rows) {
            top.i = 0;
            top.j ++;
         }
         if (states.contains(state))
            qWarning() << "skipping duplicate index" << state.index;
         else if (states.size() == depthLimit)
            qWarning() << "stack is full, skipping index" << state.index;
         else {
            states.push(state);
            maxDepth = std::max(maxDepth, states.size());
         }
      } else
         states.pop();
   }
   qDebug() << "max depth" << maxDepth << "iterations" << iterations;
   return states.isEmpty() ? QModelIndex() : states.top().index;
}

QModelIndex findIndexByString(const QAbstractItemModel* model, const QString& needle, const QModelIndex& parent = {}) {
   return findIndexByValue(model, QVariant::fromValue(needle), parent);
}
QModelIndex findIndexByString2(const QAbstractItemModel* model, const QString& text,  const QModelIndex& parent = QModelIndex());
QModelIndex findIndexByString2(const QModelIndex& index, const QString& text) {
   if (index.data().toString() == text)
      return index;
   auto model = index.model();
   if (!model || !model->hasChildren(index))
      return {};
   return findIndexByString2(model, text, index);
}

QModelIndex findIndexByString2(const QAbstractItemModel* model, const QString& text,  const QModelIndex& parent) {
   int rows = model->rowCount(parent);
   int cols = model->columnCount(parent);
   for (int i = 0; i < rows; ++i)
      for (int j = 0; j < cols; ++j) {
         auto child = findIndexByString2(model->index(i, j, parent), text);
         if (child.isValid())
            return child;
      }
   return {};
}
struct Pos {
   int row, col;
   QString toString() const { return QStringLiteral("(%1,%2)").arg(row).arg(col); }
};
Q_DECLARE_TYPEINFO(Pos, Q_PRIMITIVE_TYPE);
using Path = QVector<Pos>;
struct Builder {
   QStandardItemModel * model;
   QStringList paths;
   Path add(Path path, const Pos & pos) {
      auto item = model->invisibleRootItem();
      path.push_back(pos);
      QString pathString;
      for (auto p : path) {
         pathString += p.toString();
         auto child = item->child(p.row, p.col);
         if (!child) {
            item->setChild(p.row, p.col, child = new QStandardItem(pathString));
            paths << pathString;
         }
         item = child;
      }
      return path;
   }
   explicit Builder(QStandardItemModel * model) : model(model) {}
};
int main() {
   QStandardItemModel model;
   Builder b(&model);
   Path path, ____;
   path = b.add({}, {1, 0});     // *(1,0)
   ____ = b.add(path, {1, 1});   //  (1,0)-(1,1)
   ____ = b.add(path, {0, 0});   //  (1,0)-(0,0)
   path = b.add({}, {1, 1});     // *(1,1)
   path = b.add(path, {3, 3});   // *(1,1)-(3,3)
   ____ = b.add(path, {0, 0});   //  (1,1)-(3,3)-(0,0)
   path.pop_back();              // -(1,1)
   path = b.add(path, {2, 2});   // *(1,1)-(2,2)
   ____ = b.add(path, {0, 1});   //  (1,1)-(2,2)-(0,1)
   path = b.add({}, {2, 1});     // *(2,1)

   IndexSet indexes;
   for (auto path : b.paths) {
      auto index = findIndexByString(b.model, path);
      auto index2 = findIndexByString2(b.model, path);
      Q_ASSERT(index.isValid());
      Q_ASSERT(!indexes.contains(index));
      Q_ASSERT(index2 == index);
      indexes.insert(index);
   }
}