QtConcurrent mapped with index - qt

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.

Related

in qtXml library, why qt use qhash to store xml element attributes Instead of QMap?

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.

How to define and execute an array of functions on Sycl+openCL+DPCPP

In my program, I defined an array of functions
#include <CL/sycl.hpp>
#include <iostream>
#include <tbb/tbb.h>
#include <tbb/parallel_for.h>
#include <vector>
#include <string>
#include <queue>
#include<tbb/blocked_range.h>
#include <tbb/global_control.h>
#include <chrono>
using namespace tbb;
template<class Tin, class Tout, class Function>
class Map {
private:
Function fun;
public:
Map() {}
Map(Function f):fun(f) {}
std::vector<Tout> operator()(bool use_tbb, std::vector<Tin>& v) {
std::vector<Tout> r(v.size());
if(use_tbb){
// Start measuring time
auto begin = std::chrono::high_resolution_clock::now();
tbb::parallel_for(tbb::blocked_range<Tin>(0, v.size()),
[&](tbb::blocked_range<Tin> t) {
for (int index = t.begin(); index < t.end(); ++index){
r[index] = fun(v[index]);
}
});
// Stop measuring time and calculate the elapsed time
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
printf("Time measured: %.3f seconds.\n", elapsed.count() * 1e-9);
return r;
} else {
sycl::queue gpuQueue{sycl::gpu_selector()};
sycl::range<1> n_item{v.size()};
sycl::buffer<Tin, 1> in_buffer(&v[0], n_item);
sycl::buffer<Tout, 1> out_buffer(&r[0], n_item);
gpuQueue.submit([&](sycl::handler& h){
//local copy of fun
auto f = fun;
sycl::accessor in_accessor(in_buffer, h, sycl::read_only);
sycl::accessor out_accessor(out_buffer, h, sycl::write_only);
h.parallel_for(n_item, [=](sycl::id<1> index) {
out_accessor[index] = f(in_accessor[index]);
});
}).wait();
}
return r;
}
};
template<class Tin, class Tout, class Function>
Map<Tin, Tout, Function> make_map(Function f) { return Map<Tin, Tout, Function>(f);}
typedef int(*func)(int x);
//define different functions
auto function = [](int x){ return x; };
auto functionTimesTwo = [](int x){ return (x*2); };
auto functionDivideByTwo = [](int x){ return (x/2); };
auto lambdaFunction = [](int x){return (++x);};
int main(int argc, char *argv[]) {
std::vector<int> v = {1,2,3,4,5,6,7,8,9};
//auto f = [](int x){return (++x);};
//Array of functions
func functions[] =
{
function,
functionTimesTwo,
functionDivideByTwo,
lambdaFunction
};
for(int i = 0; i< sizeof(functions); i++){
auto m1 = make_map<int, int>(functions[i]);
//auto m1 = make_map<int, int>(f);
std::vector<int> r = m1(true, v);
//print the result
for(auto &e:r) {
std::cout << e << " ";
}
}
return 0;
}
instead of each time defining a function, I am interested in defining an array of functions and then execute it in my program. But in the part of SYCL for executing on GPU, I have an error and I do not know how to fix it.
The ERROR:
SYCL kernel cannot call through a function pointer
In particular, SYCL device code, as defined by this specification, does not support virtual function calls, function pointers in general, exceptions, runtime type information or the full set of C++ libraries that may depend on these features or on features of a particular host compiler. Nevertheless, these basic restrictions can be relieved by some specific Khronos or vendor extensions.
As per the sycl 2020 specification, No function pointers are allowed to be called in a SYCL kernel or any functions called by the kernel.
Please refer https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#introduction

QList generic join() function with template

I am trying to make a generic join() function for QList (like join() for QStringList) in order to make a toString() function for a QList of any type.
This function takes a QList, a separator and a function to dertermine how to print items.
Consider this code :
#include <QList>
#include <QDebug>
template <class T>
static QString join(const QList<T> &list, const QString &separator, const std::function< QString (const T &item) > toStringFunction)
{
QString out;
for(int i = 0; i<list.size(); i++)
out+= (i ? separator : "") + toStringFunction(list[i]);
return out;
}
int main(int argc, char *argv[])
{
QList <double> list;
list<<1.<<2.<<3.<<4.;
int precision = 1;
QString out = join(list, ",",[precision](const double &item)->QString{
return QString::number(item,'f',precision);
});
qDebug()<<out;
return 1;
}
Here the errors I have :
src\main.cpp(18): error C2672: 'join': no matching overloaded function found
src\main.cpp(20): error C2784: 'QString join(const QList<T> &,const QString &,const std::function<QString(const T &)>)': could not deduce template argument for 'const std::function<QString(const T &)>' from 'main::<lambda_f1fd4bbd6b8532d33a84751b7c214924>'
src\main.cpp(5): note: see declaration of 'join'
Clearly I dont care about this function, plenty of solutions to do it. But I don't understand what I am doing wrong with templates here.
could not deduce template argument ???
NB :
out = join<double>(list, ",",[precision](const double &item)->QString{
return QString::number(item,'f',precision);
});
=> Works fine
const std::function<QString(const double &item)> toStringFunction = [precision](const double &item)->QString{
return QString::number(item,'f',precision);
};
out = join(list, ",",toStringFunction);
=> Works fine
I'm not sure what's going on with the C++ internals, but it does work with this declaration:
template <class T>
static QString join(const QList<T> &list,
const QString &separator,
const std::function< QString (const typename QList<T>::value_type &) > toStringFunction)
I think QList can determine the template type from the list being passed, while the join template itself can't.

How to convert QList<QVariant> to QList<T>

For my serialization method i need to store a QList<T> where T is my custom Type, in a QVariantList.
QList<T> l;
l.append(T());
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
var.value<QList<T>>(); //returns an empty list which is not my serialized list;
My problem is that i cannot convert back the variant list into QList<T>
EDIT:
#define PROPERTY(type, name) \
Q_PROPERTY(type name MEMBER name) \
type name;
class Measurement
{
Q_GADGET
public:
PROPERTY(int, index)
PROPERTY(QString, name)
PROPERTY(QString, unit)
PROPERTY(double, factor)
PROPERTY(bool, isVisible)
PROPERTY(quint8, decimal)
bool operator ==(const Measurement &other)
{
return (this->index == other.index);
}
};
you can consider this class as my custom type (T). i also save the class name (here "Measurement") along with serialized data for furthur uses, because as you know we can get the registered type with QMetaType::type(char*) but with that type i can only construct a QVariant with QVariant(int typeId, const void *copy) but here i want to construct the QList<Measurement> itself.
You will need to deserialize the QVariant list one item at a time. I am also not sure that this line:
var = list;
is performing what you intended. It will take your QVariantList list and wrap it inside another QVariant called var, which is of type QVariant(QVariantList, (QVariant(MyType, ), QVariant(MyType, ))). There doesn't seem to be much benefit to doing this.
Nonetheless, the example below shows a way to recover the list from var.
#include <QCoreApplication>
#include <QVariant>
class MyType {
public:
MyType() {}
MyType(QString value) { m_value = value; }
QString m_value;
};
Q_DECLARE_METATYPE(MyType)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<MyType> l;
l.append(MyType("foo"));
l.append(MyType("bar"));
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
QList<MyType> deserializedList;
foreach(QVariant v, var.value<QVariantList>()) {
deserializedList << v.value<MyType>();
}
return a.exec();
}

How can i make a QList<QVector3D> unique

I have a QList consist of QVector3D. A QVector3D represents a vertex or a point. This List holds also all vertices of a STL-File. The problem is that a vertex exist multiple times in the list. In need a list of the unique vertices of a STL-File. How can i implement it with Qt 5.0.2?
QSet uses a hash-function for ensuring the uniqueness of the value (QMap uses operator <)
There is no qHash implementation for QVector3D in Qt.
You could implement your own one e.g. as in example:
//place anywhere in Qt-code
#include <QSet>
#include <QVector3D>
#include <QList>
uint qHash(const QVector3D &v)
{
return qHash( QString( "%1x%2x%3" ).arg(v.x()).arg(v.y()).arg(v.z()) ) ;
}
int foo()
{
QList<QVector3D> uvector3D_1;
QSet<QVector3D> uvector3D_2;
uvector3D_2 = QSet<QVector3D>::fromList(uvector3D_1);
return 0;
}
static int testFoo = foo();
Of cause it is not the fastest one, it relies on Qt's function qHash for QString. But I think it's good for demonstration.
QList<QVector3D> originalVector = ...;
then either:
QSet<QVector3D> noDublicatesSet = QSet<QVector3D>::fromList(originalVector);
or
QSet<QVector3D> noDublicatesSet = originalVector.toSet();
also you can add something like if you need QList back..
QList<QVector3D> destinationVector = QList<QVector3D>::fromSet(noDublicatesSet);
you also will need those things (sorry has them in my code for ages.. forgot that they are external).. you might want to change hash function:
#define ROTL10(x) (((x) << 10) | (((x) >> 22) & 0x000000ff))
#define ROTL20(x) (((x) << 20) | (((x) >> 12) & 0x0000ffff))
uint qHash(double data)
{
union U {
quint64 n;
double f;
};
U u;
u.f = data;
return u.f;
}
inline uint qHash(const QVector3D &v, uint seed)
{
return qHash(v.x()) ^ ROTL10(qHash(v.y())) ^ ROTL20(qHash(v.z()));
}
P.S. that's a code for Qt 5.0, actually to add missing qHash() for vectors, that's why they dont fit in QSet/QHash by default
Starting from Qt 5.14, you can use the new constructor:
template <typename InputIterator> QSet::QSet(InputIterator first, InputIterator last
Here is an example taken from the docs:
// For example, if you have code like
QStringList list;
QSet<QString> set = QSet<QString>::fromList(list);
// you can rewrite it as
QStringList list;
QSet<QString> set(list.begin(), list.end());

Resources