I am using qtxml to write a xml file, I found that the output xml file's element attributes has different order each time I run my program.
I read the source code and found that qtxml use QHash to store element attributes, which will lead to output XML file's element attributes has different order each time I run my program.
Why not use QMap to store elements' attributes? which will produce an ordered attribute.
What's the difference between QHash and QMap in this scenario?
QMap is a Red-black tree.
QHash is implemented using a hash table
QMap is slower than QHash. QMap searches are faster than QHash with fewer than 10 items.
#include <QtCore/QtCore>
#include <unordered_map>
#ifndef CONTAINER
#error CONTAINER must be defined to QMap, QHash, std::map or std::unordered_map
#endif
namespace std {
/* std::hash specialization for QString so it can be used
* as a key in std::unordered_map */
template <class Key>
struct hash;
template <>
struct hash<QString> {
typedef QString Key;
typedef uint result_type;
inline uint operator()(const QString& s) const { return qHash(s); }
};
}
int main(int argc, char** argv)
{
if (argc < 2)
qFatal("" Missing number of element to add "");
QByteArray a = argv[1];
uint num = a.toUInt();
// creates an array of random keys
QVector<QString> strs(num);
for (int i = 0; i < num; ++i)
strs[i] = qvariant_cast<QString>(qrand());
CONTAINER<QString, QString> c;
for (uint i = 0; i < num; ++i) {
QString& k = strs[i];
c[k] = QString::number(i);
}
quint64 it = 0;
const QString* arr = strs.constData();
QElapsedTimer t;
t.start();
while (t.elapsed() < 1000) {
const QString& k = arr[(++it) * 797 % num];
c[k]; // perform a lookup
}
qDebug() << it / 1000;
}
The higher the iteration value, the best. The scale of the number of elements is logarithmic. It should be expected that for QHash the value will not change with increasing number of elements, and for QMap it should be equal to log N, which corresponds to a straight line on a logarithmic scale.
However, with a large number of elements, the results are not in favor of QMap.
This is most likely the reason why QHash is used.
From the Qt documentation about QMap::iterator :
Unlike QHash, which stores its items in an arbitrary order, QMap
stores its items ordered by key. Items that share the same key
(because they were inserted using QMap::insertMulti(), or due to a
unite()) will appear consecutively, from the most recently to the
least recently inserted value.
What I want is to interate a map by inserted index. For example this map.
const static QMap<QString, int> MEASUREMENT_COLUMNS{{"ID", MY_SQL_BIGINT}, {"logging_id", MY_SQL_INT}, {"calibration_id", MY_SQL_INT}, {"logging_comment", MY_SQL_VARCHAR255}, {"measurement_date_time", MY_SQL_DATETIME}, {"ADC0", MY_SQL_FLOAT},
{"ADC0", MY_SQL_FLOAT},
{"ADC1", MY_SQL_FLOAT},
{"ADC2", MY_SQL_FLOAT},
But the problem is as the documentation says above about QMap and QHashmap. They will not work for be if I want to iterate a map by inserted index.
For example, first ID, then logging_id, then calibration_id etc.
So I need to select something else than QMap and QHash.
Question:
Is there a map-like tool in QT that can be iterated over inserted index?
You can use two QVector, or use QVector<QPair<QString, int> > instead.
Here's the start of a QHash derivative which provides this functionality. DISCLAIMER: This is not entirely perfected! Not every function / feature of QHash has yet been accounted for. As long as you only use the functions / operator overloads provided here, you'll be fine for sure. If someone wants to keep developing this and repost a truly "finished" class, that would be great!
Note that performance will of course be degraded a bit, and memory consumption will increase, using this vs the natural QHash, but for small data sets that should be negligible.
OrderedHash.h
#ifndef ORDEREDHASH_H
#define ORDEREDHASH_H
#include <QHash>
#include <QVector>
#include <QDataStream>
#include <QDebug>
template<class K, class V>
class OrderedHash : public QHash<K,V>
{
public:
using QHash<K,V>::QHash;
#ifdef Q_COMPILER_INITIALIZER_LISTS
OrderedHash( std::initializer_list<std::pair<K, V>> list )
: QHash<K,V>::QHash()
{ foreach( auto p, list ) insert( std::get<0>(p), std::get<1>(p) ); }
#endif
// Returns the keys in the order they were inserted.
// If the ordered keys vector is blatantly out of sync with the hash
// (as may occur via the use of QHash functions not accounted for
// by this override!), this returns UNordered keys, since those are at
// least accurate.
QList<K> orderedKeys() const {
if( QHash<K,V>::size() != orderedKeys_.size() )
{
qWarning() << "OrderedHash keys are out of sync!";
return QHash<K,V>::keys();
}
return orderedKeys_.toList();
}
// This insert override "appends" to the "end" of the hash. If the key is
// already present, the entry is "moved" to the new end.
typename QHash<K,V>::iterator insert( const K &key, const V &value )
{
//qDebug() << "OrderedHash insert: " << key << ":" << value;
orderedKeys_.removeAll( key );
orderedKeys_.push_back( key );
return QHash<K,V>::insert( key, value );
}
// This additional update function perseveres the "key order" while
// modifying the value. If the key is not yet present, the entry is
// appended to the "end" of the hash.
typename QHash<K,V>::iterator update( const K &key, const V &value )
{
if( !QHash<K,V>::contains( key ) ) return insert( key, value );
return QHash<K,V>::insert( key, value );
}
int remove( const K &key )
{
orderedKeys_.removeAll( key );
return QHash<K,V>::remove( key );
}
void clear()
{
orderedKeys_.clear();
QHash<K,V>::clear();
}
private:
QVector<K> orderedKeys_;
};
// COPIED AND TWEAKED QT SOURCE FOR THESE STREAM OPERATOR OVERLOADS
template <class Key, class T>
Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, OrderedHash<Key, T> &hash)
{
QDataStream::Status oldStatus = in.status();
in.resetStatus();
hash.clear();
quint32 n;
in >> n;
for (quint32 i = 0; i < n; ++i) {
if (in.status() != QDataStream::Ok)
break;
Key k;
T t;
in >> k >> t;
/* ORGINAL QT SOURCE
hash.insertMulti(k, t);
*/
//---------------------------------
hash.insert(k, t);
//---------------------------------
}
if (in.status() != QDataStream::Ok)
hash.clear();
if (oldStatus != QDataStream::Ok)
in.setStatus(oldStatus);
return in;
}
template <class Key, class T>
Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const OrderedHash<Key, T>& hash)
{
out << quint32(hash.size());
/* ORGINAL QT SOURCE
typename QHash<Key, T>::ConstIterator it = hash.end();
typename QHash<Key, T>::ConstIterator begin = hash.begin();
while (it != begin) {
--it;
out << it.key() << it.value();
}
*/
//---------------------------------
const QList<Key> keys( hash.orderedKeys() );
foreach( auto key, keys ) out << key << hash.value(key);
//---------------------------------
return out;
}
#endif // ORDEREDHASH_H
Not in QT (to my knowledge, at least).
Can you use Boost, e.g. boost::multiindex? Another option is to combine map with vector in a class +- like this (this is likely to contain errors; it's supposed to illustrate the general idea, not to be a fully working piece of code):
template<typename K, typename V>
class indexed_map
{
map<K, V> m_map;
vector<K> m_insertionOrder;
public:
void insert(const K& k, const V& v)
{
m_map.insert(k,v);
m_insertionOrder.push_back(k);
}
V byKey(const K& k) const {return m_map.at(k)};
V byOrder(size_t n) const {return m_map.at(m_insertionOrder.at(n));}
};
Of course you'll have to write some boilerplate (ok, lots of it in fact), iterators might be also tricky.
I wondered if there is an option to also hand over the current processed index with QtConcurrent::mapped(someVector, &someFunction)) (also filter, filtered, map,...)
What I want: I want to do something with the elements in someVector based on the current index in it. but since the function someFunction is only taking the type T which is also used for the QVector<T> vector.
What I did: Because I needed this, I created a QVector<std::pair<int, T>> and manually created the index for the elements.
Since this requires more space and is not a nice solution, I thought maybe there could be another solution.
Docs: https://doc.qt.io/qt-5/qtconcurrent-index.html
If your input is a QVector, you can make use of the fact that QVector stores all the elements contiguously. This means that given a reference to an element e in a QVector v, then the index of e can be obtained by:
std::ptrdiff_t idx = &e - &v.at(0);
Below is a complete example using QtConcurrent::mapped:
#include <iterator>
#include <numeric>
#include <type_traits>
#include <utility>
#include <QtCore>
#include <QtConcurrent>
// lambda functions are not directly usable in QtConcurrent::mapped, the
// following is a necessary workaround
// see https://stackoverflow.com/a/49821973
template <class T> struct function_traits :
function_traits<decltype(&T::operator())> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
// specialization for pointers to member function
using functor_type = ClassType;
using result_type = ReturnType;
using arg_tuple = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
};
template <class Callable, class... Args>
struct CallableWrapper : Callable, function_traits<Callable> {
CallableWrapper(const Callable &f) : Callable(f) {}
CallableWrapper(Callable &&f) : Callable(std::move(f)) {}
};
template <class F, std::size_t ... Is, class T>
auto wrap_impl(F &&f, std::index_sequence<Is...>, T) {
return CallableWrapper<F, typename T::result_type,
std::tuple_element_t<Is, typename T::arg_tuple>...>(std::forward<F>(f));
}
template <class F> auto wrap(F &&f) {
using traits = function_traits<F>;
return wrap_impl(std::forward<F>(f),
std::make_index_sequence<traits::arity>{}, traits{});
}
int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv);
// a vector of numbers from 0 to 500
QVector<int> seq(500, 0);
std::iota(seq.begin(), seq.end(), 0);
qDebug() << "input: " << seq;
QFuture<int> mapped = QtConcurrent::mapped(seq, wrap([&seq](const int& x) {
// the index of the element in a QVector is the difference between
// the address of the first element in the vector and the address of
// the current element
std::ptrdiff_t idx = std::distance(&seq.at(0), &x);
// we can then use x and idx however we want
return x * idx;
}));
qDebug() << "output: " << mapped.results();
QTimer::singleShot(100, &app, &QCoreApplication::quit);
return app.exec();
}
See this question for a related discussion. Note that the linked question has a cleaner answer that involves the usage of zip and counting iterators from boost (or possibly their C++20 ranges counterparts), but I don't think that this would play well with QtConcurrent::map when map slices the sequence into blocks, and distributes these blocks to multiple threads.
I am shifting from Python to C so bit rusty on the semantics as well as coding habit. In Python everything is treated as an object and objects are passed to functions. This is not the case in C so I want to increment an integer using pointers. What is the correct assignment to do so. I want to do it the following way but have the assignments wrong:
#include <stdio.h>
int i = 24;
int increment(*i){
*i++;
return i;
}
int main() {
increment(&i);
printf("i = %d, i);
return 0;
}
I fixed your program:
#include <stdio.h>
int i = 24;
// changed from i to j in order to avoid confusion.
// note you could declare the return type as void instead
int increment(int *j){
(*j)++;
return *j;
}
int main() {
increment(&i);
printf("i = %d", i);
return 0;
}
Your main error was the missing int in the function's argument (also a missing " in the printf).
Also I would prefer using parentheses in expressions as *j++ and specify exactly the precedence like I did in (*j)++, because I want to increment the content of the variable in the 'j' location not to increment the pointer - meaning to point it on the next memory cell - and then use its content.
I need somthing similar to QSet, but I need the items to be saved on the order I inserted them
is there such thing?
I am not aware of anything like that out of the box in neither Qt nor STL. Boost has something like that I think but it is not that hard to do this yourself.
You could do a wrapper around QHash like this:
template<typename T>
class MySet : QHash<T, int>
{
public:
using QHash<T, int>::QHash;
QVector<T> values() //this 'hides' the base QHash::values() of QHash
{
QVector<T> vec(count());
for(auto it = cbegin(); it != end(); ++it)
{
vec[it.value()] = it.key();
}
return vec;
}
void insert(const T &value)
{
if(!contains(value))
{
insert(value, m_Data.count());
}
}
};
The usage is quite similar to QSet:
MySet<QString> set;
set.insert("1");
set.insert("2");
set.insert("3");
qDebug() << set.values();
And that prints the values in order. If you need more complete support like iterators also iterating in your desired order you would have to reimplement more functionality but the gist of it would be the same. After all QSet is internally QHash as well. Note that the above does not support removal without modification.
Maybe a QList or a QVector could help.
QList<QString> stringList;
//By the way, Qt provides QStringList as a typedef for QList<QString>
stringList.append("A");
stringList.append("B");
qDebug() << stringList.at(0); //A
qDebug() << stringList.at(1); //B