Как хэшировать QVariant?

Мне нужно использовать QList<QVariant> как ключ к std::unordered_map, Целью этого является оптимизация поиска по таблице данных путем создания индекса по столбцам уникальных ключей.

Итак, я сделал этот код. Это не полный, но перечисляет некоторые основные типы данных, которые встречаются в ключевых столбцах таблицы:

#include <unordered_map>
#include <string>
//std::hash
#include <functional>
//std::size_t
#include <cstddef>
// Hashing method for QVariantList
namespace std {
    template <>
    struct hash<QList<QVariant>>
    {
        std::size_t operator()(const QList<QVariant>& k) const
        {
            using std::size_t;
            using std::hash;
            using std::string;
            size_t hash_num = 0;
            Q_FOREACH(var, k) {
                // Make hash of the primitive value of the QVariant
                switch(var.type()) {
                    case QVariant::String : {
                        hash_num = hash_num^hash<string>(var.toString().toStdString());
                        break;
                    }
                    case QVariant::Char :
                    case QVariant::ULongLong :
                    case QVariant::UInt :
                    case QVariant::LongLong :
                    case QVariant::Int : {
                        hash_num = hash_num^hash<long long>(var.toLongLong());
                        break;
                    }
                    case QVariant::Double : {
                        hash_num = hash_num^hash<double>(var.toDouble());
                        break;
                    }
                }
            }
            return hash_num;
        }
    };
}

Очевидно, я не люблю весь switch вещь. Это довольно длинный и уродливый код, который учитывает только основные типы. Я предпочел бы сделать хэш данных памяти, выделенных для QVariantвнутренние данные. Или, что еще лучше, используйте метод хеширования в Qt.

Есть ли полунадежный * способ хеширования любого QVariant без преобразования его в примитивный тип?

* Я понимаю, что сложные объекты могут скрываться за QVariant, но случаи, когда это может привести к столкновению, достаточно редки, поэтому мне не нужно об этом беспокоиться.

1 ответ

Решение

Получить себе QByteArray + QBuffer + QDataStream в основном сериализовать QVariantс QByteArray,

Затем просто хэшируйте необработанные байты в байтовом массиве. Qt уже реализует qHash функция для QByteArray так что все готово.

Вы можете максимизировать эффективность, используя то же самое QByteArray с достаточным количеством предварительно распределенных байтов, чтобы избежать перераспределения. Вы можете обернуть все это в VariantHasher класс и просто seek(0) для буфера перед каждым новым хэшированием и только pos() количество байтов вместо всего этого.

class QVariantHasher {
  public:
    QVariantHasher() : buff(&bb), ds(&buff) {
      bb.reserve(1000);
      buff.open(QIODevice::WriteOnly);
    }
    uint hash(const QVariant & v) {
      buff.seek(0);
      ds << v;
      return qHashBits(bb.constData(), buff.pos());
    }
  private:
    QByteArray bb;
    QBuffer buff;
    QDataStream ds;
};

Это довольно быстро, как упомянуто в комментариях, и имеет преимущество работы с каждым типом, который поддерживает QDataStream сериализации. Для пользовательских типов вам нужно только реализовать сериализацию, не нужно создавать и поддерживать гигантский коммутатор. Если у вас уже есть реализованная версия коммутатора, было бы интересно сделать сравнение. Сам переключатель имеет много ветвлений, в то время как повторное использование одного и того же байтового массива очень удобно для кэша, особенно если вы не используете много байтов, то есть вы не хэшируете варианты, которые содержат очень длинные строки или массивы.

Кроме того, это лучше, чем полустабильность, поскольку хеширование также включает тип варианта, поэтому даже в тех случаях, когда фактические данные могут быть двоичными идентичными, например, два байта со значениями 255 против короткого значения со значением 65535, хеш будет включить тип, чтобы значения не сталкивались.

Другие вопросы по тегам