How to properly use qRegisterMetaType with multidimensional arrays? - qt

I want to use something like
typedef double Matrix[4][4];
to represent transformations and also pass them around with the QT signal/slot mechanism. But when I use
Q_DECLARE_METATYPE(Matrix)
it throws an error in qmetatype.h at this function
void *qMetaTypeConstructHelper(const T *t)
{
if (!t)
return new T();
return new T(*static_cast<const T*>(t));
}
saying: "error C2075: 'Target of operator new()' : array initialization needs curly braces"

Q_DECLARE_METATYPE(T) requires the type T to be default-constructable, copiable and destructable. Your Matrix type is not copiable, thus you can't use Q_DECLARE_METATYPE on it.
Workaround: use a class.
// copiable, C++98 brace-initializable, etc.
struct Matrix {
double data[4][4];
};

Ideally you should be using eigen3 and its types. Alternatively, wrap your type in a class. Once you do it, you might as well have the class do more than be a mere wrapper. Eventually, you'll see that eigen3 is the only sane way to proceed. Probably when you get to this point:
#include <cstring>
class Matrix {
double m_data[4][4];
public:
typedef double (*Data)[4];
Matrix() {}
Matrix(const Matrix & other) { memcpy(m_data, other.m_data, sizeof m_data); }
Matrix & operator=(const Matrix & other) { memcpy(m_data, other.m_data, sizeof m_data); return *this; }
Matrix & operator=(const Data other) { memcpy(m_data, other, sizeof m_data); return *this; }
operator Data() { return m_data; }
};
int main()
{
double mat1[4][4];
Matrix mat2;
mat2[3][3] = 1;
mat2 = mat1;
return 0;
}

Related

Is there a map-like tool in QT that can be iterated over inserted index?

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.

Correct Assignment for Pointers

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.

Qt: Trying to pass multiple references to a slot function after a signal is emitted

EDIT: I have read Passing and argument to a slot it's helpful, but doesn't address my issue of passing multiple references to a function I called via a signal-slot.
I'm currently working on a Qt application that essentially is a unit converter. I'm implementing it using QDoubleSpinBoxes as the input and the output. I am running into an issue that i'm looking for help with. The implementation idea is that the user will input a value of whatever they want to convert and it will, upon the user losing focus on the spinbox or hitting enter, populate the other unit type boxes with the answer.
Here is how I currently do it:
// creates a custom spinbox for each option
modifiedSpinbox *FahrenheitDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *CelciusDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *KelvinDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *RankineDblSpinbox = new modifiedSpinbox();
// Creates a signal mapper that allows passing of a parameter
// to the convert functions anytime a number is entered
QSignalMapper *tempMapper = new QSignalMapper(this);
// Connects the spinbox editing complete signal with the mapper
connect(tempMapper, SIGNAL(mapped(int)),
this,SLOT(on_userInput(int whoSignaled)));
// Connects the mapper with the function that calls the convert class
connect(FahrenheitDblSpinbox, SIGNAL(editingFinished()),
tempMapper, SLOT(map()));
tempMapper->setMapping(FahrenheitDblSpinbox, 1);
The way I would like to implement this conversion function is to, on user finishing their input into a spinbox, have the code send a signal (editingFinished()) to a slot function which calls a converttools class that has the functions needed to do the actual conversion. The problem that i'm running in to is that I cannot figure out how to pass references for these spinbox objects to my converttools class so that I can set the spinbox values directly.
The closest i've come is to use QSignalMapper (seen above) to pass a single int or Qwidget to my slot function, not the objects I want.
I would like some advice as to how to pass multiple references to my custom class after a signal is emitted. I've looked though numerous questions here and still cant seem to figure out how to do this or a better way i'm not seeing.
Thanks!
QSignalMapper is obsolete in C++11. It's only a band-aid against the cumbersomeness of declaring functors in C++98. If you're using a C++11 compiler, you never have to use QSignalMapper.
Lambdas make it trivial - and you really don't need to pass much. Side note: ideally, you should use a modern C++11 units library.
Here's a complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/tempconvert-42648860
#include <QtWidgets>
const char kUnit[] = "unit";
class MyWidget : public QWidget {
QFormLayout m_layout{this};
QDoubleSpinBox m_fahrenheit, m_celsius, m_kelvin;
QList<QDoubleSpinBox*> const m_spinBoxes{&m_fahrenheit, &m_celsius, &m_kelvin};
enum Unit { Fahrenheit, Celsius, Kelvin };
static double Q_DECL_RELAXED_CONSTEXPR fromK(Unit to, double val) {
if (to == Fahrenheit) return (val-273.15)*1.8 + 32.0;
else if (to == Celsius) return val - 273.15;
else return val;
}
static double Q_DECL_RELAXED_CONSTEXPR toK(Unit from, double val) {
if (from == Fahrenheit) return (val-32.0)/1.8 + 273.15;
else if (from == Celsius) return val + 273.15;
else return val;
}
void setTemp(Unit unit, double temp, QDoubleSpinBox * skip = nullptr) {
for (auto spin : m_spinBoxes) if (spin != skip) {
QSignalBlocker b{spin};
spin->setValue(fromK(unitOf(spin), toK(unit, temp)));
}
}
static Unit unitOf(QDoubleSpinBox * spin) {
return static_cast<Unit>(spin->property(kUnit).toInt());
}
public:
MyWidget(QWidget * parent = nullptr) : QWidget{parent} {
m_layout.addRow("Fahreneheit", &m_fahrenheit);
m_layout.addRow("Celsius", &m_celsius);
m_layout.addRow("Kelvin", &m_kelvin);
m_fahrenheit.setProperty(kUnit, Fahrenheit);
m_celsius.setProperty(kUnit, Celsius);
m_kelvin.setProperty(kUnit, Kelvin);
for (auto const spin : m_spinBoxes) {
auto const unit = unitOf(spin);
spin->setRange(fromK(unit, 0.), fromK(unit, 1000.));
connect(spin, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
[=]{ setTemp(unit, spin->value(), spin); });
}
setTemp(Celsius, 20.);
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MyWidget ui;
ui.show();
return app.exec();
}
What you're looking for is:
Signals to carry the QDoubleSpinBox's assigned temperature
A conversion to a common temperature unit
A Signal to all other QDoubleSpinBoxs to update them
A conversion from common temperature unit to each QDoubleSpinBox's specific temperature unit
QSignalMapper is a bad choice here, because:
This class collects a set of parameterless signals, and re-emits them with integer, string or widget parameters corresponding to the object that sent the signal
So we cannot take in the assigned temperature. Instead lets start with a map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> which will serve to map from a given QDoubleSpinBox to its "conversion to a common temperature unit" and "conversion from a common temperature unit", respectively.
We'll then build an object around this map looking something like this:
class SlotMapper : public QObject
{
Q_OBJECT
map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> mapping;
public:
SlotMapper() = default;
SlotMapper(const map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> mapping) : mapping(mapping) {};
AddMapping(QDoubleSpinBox* key, function<double(double)> valueFirst, function<double(double)> valueSecond) { mapping.insert_or_assign(key, make_pair(valueFirst, valueSecond)); }
void map(const double assignedTemperature) const {
const auto commonTemperatureUnit = mapping.at(QObject()::sender).first(assignedTemperature);
for(auto it = cbegin(mapping); it != cend(mapping); ++it) {
if(it->first != QObject()::sender) {
it->first->blockSignals(true);
it->first->setValue(it->second.second(commonTemperatureUnit));
it->first->blockSignals(false);
}
}
}
};
This object should be constructed with all necessary conversion functions. in your case that probably looks something like:
SlotMapper mySlotMapper(map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>>{ {FahrenheitDblSpinbox, make_pair([](const double param){ return (param - 32.0) * 5.0 / 9.0; }, [](const double param){ return param * 9.0 / 5.0 + 32.0; })},
{CelciusDblSpinbox, make_pair([](const double param){ return param; }, [](const double param){ return param; })},
{KelvinDblSpinbox, make_pair([](const double param){ return param - 273.15; }, [](const double param){ return param + 273.15; })},
{RankineDblSpinbox, make_pair([](const double param){ return (param - 491.67) * 5.0 / 9.0; }, [](const double param){ return (param + 273.15) * 9.0 / 5.0; })} });
As far as your connections, they'll look like:
connect(FahrenheitDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(CelciusDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(KelvinDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(RankineDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);

Program fails when trying to add a pointer to an array inside a function (C)

I cannot get this code to work properly. When I try to compile it, one of three things will happen: Either I'll get no errors, but when I run the program, it immediately locks up; or it'll compile fine, but says 'Segmentation fault' and exits when I run it; or it gives warnings when compiled:
"conflicting types for ‘addObjToTree’
previous implicit declaration of ‘addObjToTree’ was here"
but then says 'Segmentation fault' and exits when I try to run it.
I'm on Mac OS X 10.6 using gcc.
game-obj.h:
typedef struct itemPos {
float x;
float y;
} itemPos;
typedef struct gameObject {
itemPos loc;
int uid;
int kind;
int isEmpty;
...
} gameObject;
internal-routines.h:
void addObjToTree (gameObject *targetObj, gameObject *destTree[]) {
int i = 0;
int stop = 1;
while (stop) {
if ((*destTree[i]).isEmpty == 0)
i++;
else if ((*destTree[i]).isEmpty == 1)
stop = 0;
else
;/*ERROR*/
}
if (stop == 0) {
destTree[i] = targetObj;
}
else
{
;/*ERROR*/
}
}
/**/
void initFS_LA (gameObject *target, gameObject *tree[], itemPos destination) {
addObjToTree(target, tree);
(*target).uid = 12981;
(*target).kind = 101;
(*target).isEmpty = 0;
(*target).maxHealth = 100;
(*target).absMaxHealth = 200;
(*target).curHealth = 100;
(*target).skill = 1;
(*target).isSolid = 1;
(*target).factionID = 555;
(*target).loc.x = destination.x;
(*target).loc.y = destination.y;
}
main.c:
#include "game-obj.h"
#include "internal-routines.h"
#include <stdio.h>
int main()
{
gameObject abc;
gameObject jkl;
abc.kind = 101;
abc.uid = 1000;
itemPos aloc;
aloc.x = 10;
aloc.y = 15;
gameObject *masterTree[3];
masterTree[0] = &(abc);
initFS_LA(&jkl, masterTree, aloc);
printf("%d\n",jkl.factionID);
return 0;
}
I don't understand why it doesn't work. I just want addObjToTree(...) to add a pointer to a gameObject in the next free space of masterTree, which is an array of pointers to gameObject structures. even weirder, if I remove the line addObjToTree(target, tree); from initFS_LA(...) it works perfectly. I've already created a function that searches masterTree by uid and that also works fine, even if I initialize a new gameObject with initFS_LA(...) (without the addObjToTree line.) I've tried rearranging the functions within the header file, putting them into separate header files, prototyping them, rearranging the order of #includes, explicitly creating a pointer variable instead of using &jkl, but absolutely nothing works. Any ideas? I appreciate any help
If I see this correctly, then you don't initialize elements 1 and 2 of the masterTree array anywhere. Then, your addObjToTree() function searches the - uninitialized - array for a free element.
Declaring a variable like gameObject *masterTree[3]; in C does not zero-initialize the array. Add some memset (masterTree, 0, sizeof (masterTree)); to initialize.
Note that you're declaring an array of pointers to structs here, not an array of structs (see also here), so you also need to adjust your addObjToTree() to check for a NULL-pointer instead of isEmpty.
It would also be good practice to pass the length of that array to that function to avoid buffer overruns.
If you want an array of structs, then you need to declare it as gameObject masterTree[3]; and the parameter in your addObjToTree() becomes gameObject *tree.

Structure of vectors C++

is there a way to clear a structure of vectors at a time using a single statement ?
i.e.
struct AStruct
{
vector StringList;
vector DistanceList;
}A;
i want both the vectors using a single statement.
Sure:
AStruct a;
// stuff
a = AStruct(); // clear it
However, I would probably give myself a function:
struct AStruct {
vector <string> StringList;
vector <string> DistanceList; }
void clear() {
StringList.clear();
DistanceList.clear();
}
};
You can then say:
AStruct a;
// stuff
a.clear(); // clear it
which is perhaps easier to understand.

Resources