遍历所有模型索引及其子索引会导致堆栈溢出错误
Looping over all model indexes and their children causes stack overflow error
我创建了以下函数来遍历 table 或树中的所有索引并找到包含所需字符串的索引:
#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);
}
此代码是自包含的,在 Qt 库之外没有任何依赖项。
我在国外代码中循环某些模型时遇到堆栈溢出错误。
我正在尝试创建通用的正确算法来执行此操作。以上算法正确吗?
代码看起来不错。这是经典的深度优先搜索。 它运行出栈了,因为你的模型不是树,而是循环图and/or它有无限的深度。
我们可以使用显式堆栈重新制定搜索,并添加测试以确保面对格式错误的模型时的合理行为。
如果您的模型在设计上具有无限深度或循环,下面的代码将适当地执行并且不会 运行 出栈。
// https://github.com/KubaO/Whosebugn/tree/master/questions/model-find-diagnostic-44416637
#include <QtGui>
首先,我们需要一组索引,因为 QSet
仅适用于我们不使用的持久索引:
class IndexSet : public QMap<QModelIndex, bool> {
public:
void insert(const QModelIndex & key) { QMap::insert(key, true); }
};
查找器的状态,我们将保留在显式堆栈中:
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 << "}";
}
然后,一个查找实用程序可验证一切正常且模型没有异常行为:
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>;
构建器将项目添加到模型以确保给定路径有效。每个项目的值都是一个字符串,将其路径表示为构成路径 (row,col)(row,col)...
的位置的串联。当构建器构建模型时,它还会保留它创建的所有项目的路径。
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);
}
}
示例到此结束。
我创建了以下函数来遍历 table 或树中的所有索引并找到包含所需字符串的索引:
#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);
}
此代码是自包含的,在 Qt 库之外没有任何依赖项。
我在国外代码中循环某些模型时遇到堆栈溢出错误。
我正在尝试创建通用的正确算法来执行此操作。以上算法正确吗?
代码看起来不错。这是经典的深度优先搜索。 它运行出栈了,因为你的模型不是树,而是循环图and/or它有无限的深度。
我们可以使用显式堆栈重新制定搜索,并添加测试以确保面对格式错误的模型时的合理行为。
如果您的模型在设计上具有无限深度或循环,下面的代码将适当地执行并且不会 运行 出栈。
// https://github.com/KubaO/Whosebugn/tree/master/questions/model-find-diagnostic-44416637
#include <QtGui>
首先,我们需要一组索引,因为 QSet
仅适用于我们不使用的持久索引:
class IndexSet : public QMap<QModelIndex, bool> {
public:
void insert(const QModelIndex & key) { QMap::insert(key, true); }
};
查找器的状态,我们将保留在显式堆栈中:
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 << "}";
}
然后,一个查找实用程序可验证一切正常且模型没有异常行为:
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>;
构建器将项目添加到模型以确保给定路径有效。每个项目的值都是一个字符串,将其路径表示为构成路径 (row,col)(row,col)...
的位置的串联。当构建器构建模型时,它还会保留它创建的所有项目的路径。
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);
}
}
示例到此结束。