How to use C++11 range-based for with QJsonArray - qt

With old-style loops, I can delve into a QJsonArray and, in the example below, add element "foo" with contents of existing element "bar" for each array item. How can I do this using C++11 range-based for?
// QJsonArray oldArray contains an array of which one element is "bar"
QJsonArray newArray;
int i, b = oldArray.count();
for (i=0; i<n; ++i) {
QJsonObject element = oldArray.at(i).toObject();
element["foo"] = element["bar"];
newArray.append(element);
}
I have tried the following (admittedly as trial and error):
auto&
for (auto& element : oldArray) {
element["foo"] = element["bar];
newArray.append(element);
}
I get the error
non-const lvalue reference to type 'QJsonValueRef' cannot bind to a temporary of type 'QJsonValueRef'
const auto&
for (const auto& element : oldArray) {
...
I get a warning
loop variable 'element' is always a copy because the range of type 'QJsonArray' does not return a reference
const auto
for (const auto element : oldArray) {
element["foo"] = element["bar];
...
I get the error
no viable overloaded operator[] for type 'const QJsonValueRef'
relating to element["bar"]

The problem is that the iterator for QJsonArray returns a temporary QJsonValueRef object, and lvalue references can not bind to temporaries. We can either hold that temporary by value:
// QJsonArray oldArray contains an array of which one element is "bar"
QJsonArray newArray;
for (auto v : oldArray) {
QJsonObject element = v.toObject();
element["foo"] = element["bar"];
newArray.append(element);
}
In this case v is a QJsonValueRef object (similar to what oldArray.at(i) gives in the old-style loop). After that, we convert that QJsonValueRef to a QJsonObject using .toObject().
Or we can use forwarding references since they can bind to rvalues:
for (auto&& v : oldArray) {
...
}
In this case v is deduced to be an rvalue reference to a QJsonValueRef.
Both solutions are identical in terms of the number of objects being created/destructed (since in the former, the copy is elided under C++17 guaranteed copy elision rules or even in pre-C++17 when using any decent compiler. In the latter, the reference is bound to the temporary and this prolongs its lifetime to match the whole iteration).
Notes
This problem is similar to what happens when using range-based-for with a std::vector<bool>; since both std::vector<bool> and QJsonArrayhave iterators that return proxy objects.
If using the Clang code model, the second solution generates the warning "loop variable 'v' is always a copy because the range of type 'QJsonArray' does not return a reference". See this question for an explanation.

Related

Will an array of pointers be equal to an array of chars?

I have got this code:
import std.stdio;
import std.string;
void main()
{
char [] str = "aaa".dup;
char [] *str_ptr;
writeln(str_ptr);
str_ptr = &str;
*(str_ptr[0].ptr) = 'f';
writeln(*str_ptr);
writeln(str_ptr[0][1]);
}
I thought that I am creating an array of pointers char [] *str_ptr so every single pointer will point to a single char. But it looks like str_ptr points to the start of the string str. I have to make a decision because if I am trying to give access to (for example) writeln(str_ptr[1]); I am getting a lot of information on console output. That means that I am linking to an element outside the boundary.
Could anybody explain if it's an array of pointers and if yes, how an array of pointers works in this case?
What you're trying to achieve is far more easily done: just index the char array itself. No need to go through explicit pointers.
import std.stdio;
import std.string;
void main()
{
char [] str = "aaa".dup;
str[0] = 'f';
writeln(str[0]); // str[x] points to individual char
writeln(str); // faa
}
An array in D already is a pointer on the inside - it consists of a pointer to its elements, and indexing it gets you to those individual elements. str[1] leads to the second char (remember, it starts at zero), exactly the same as *(str.ptr + 1). Indeed, the compiler generates that very code (though plus range bounds checking in D by default, so it aborts instead of giving you gibberish). The only note is that the array must access sequential elements in memory. This is T[] in D.
An array of pointers might be used if they all the pointers go to various places, that are not necessarily in sequence. Maybe you want the first pointer to go to the last element, and the second pointer to to the first element. Or perhaps they are all allocated elements, like pointers to objects. The correct syntax for this in D is T*[] - read from right to left, "an array of pointers to T".
A pointer to an array is pretty rare in D, it is T[]*, but you might use it when you need to update the length of some other array held by another function. For example
int[] arr;
int[]* ptr = &arr;
(*ptr) ~= 1;
assert(arr.length == 1);
If ptr wasn't a pointer, the arr length would not be updated:
int[] arr;
int[] ptr = arr;
ptr ~= 1;
assert(arr.length == 1); // NOPE! fails, arr is still empty
But pointers to arrays are about modifying the length of the array, or maybe pointing it to something entirely new and updating the original. It isn't necessary to share individual elements inside it.

fill nested std::map with insert operator

I have the following code:
#include <iostream>
#include <utility>
#include <map>
using namespace std;
int main()
{
map<int, map<string, int> > mapa;
// way A
mapa[10]["aaa"] = 20;
// way B -> Compilation Error
pair<int, pair<string, int> > par(10, make_pair("aaa", 20));
mapa.insert(par);
return 0;
}
I know that "way A" of populating the map works.
I want to use "way B" but it throw a compilation Error:
error: no matching function for call to ‘std::map, int>::map(const std::pair, int>&)’
How can I populate the nested map with insert operator.
Pd: I don't use [] operator because it requires the default constructor to be defined which I don't have since I am using time_period objects from Boost.
Well the type of your map is map of (int -> map of (string -> int)) but you are trying to insert an entry of type map of (int -> pair (string, int)). A pair is not a map, thus the error.
EDIT:
According to the documentation, a call to map's [] operator is equivalent to a series of other operations:
mapped_type& operator[] (const key_type& k);
A call to this function is equivalent to:
(*((this->insert(make_pair(k,mapped_type()))).first)).second
so in your case, the call mapa[10]["aaa"] = 20; is equivalent to:
(*(( (*((mapa.insert(make_pair(10,map<string, int>()))).first)).second
.insert(make_pair("aaa",20))).first)).second
but I believe if either key 10 or aaa exist, no element will be inserted in the map. I suggest you read the docs thoroughly and test for the expected behavior.

QMap Memory Error

I am doing one project in which I define a data types like below
typedef QVector<double> QFilterDataMap1D;
typedef QMap<double, QFilterDataMap1D> QFilterDataMap2D;
Then there is one class with the name of mono_data in which i have define this variable
QFilterMap2D valid_filters;
mono_data Scan_data // Class
Now i am reading one variable from a .mat file and trying to save it in to above "valid_filters" QMap.
Qt Code: Switch view
for(int i=0;i<1;i++)
{
for(int j=0;j<1;j++)
{
Scan_Data.valid_filters[i][j]=valid_filters[i][j];
printf("\nValid_filters=%f",Scan_Data.valid_filters[i][j]);
}
}
The transferring is done successfully but then it gives run-time error
Windows has triggered a breakpoint in SpectralDataCollector.exe.
This may be due to a corruption of the heap, and indicates a bug in
SpectralDataCollector.exe or any of the DLLs it has loaded.
The output window may have more diagnostic information
Can anyone help in solving this problem. It will be of great help to me.
Thanks
Different issues here:
1. Using double as key type for a QMap
Using a QMap<double, Foo> is a very bad idea. the reason is that this is a container that let you access a Foo given a double. For instance:
map[0.45] = foo1;
map[15.74] = foo2;
This is problematic, because then, to retrieve the data contained in map[key], you have to test if key is either equal, smaller or greater than other keys in the maps. In your case, the key is a double, and testing if two doubles are equals is not a "safe" operation.
2. Using an int as key while you defined it was double
Here:
Scan_Data.valid_filters[i][j]=valid_filters[i][j];
i is an integer, and you said it should be a double.
3. Your loop only test for (i,j) = (0,0)
Are you aware that
for(int i=0;i<1;i++)
{
for(int j=0;j<1;j++)
{
Scan_Data.valid_filters[i][j]=valid_filters[i][j];
printf("\nValid_filters=%f",Scan_Data.valid_filters[i][j]);
}
}
is equivalent to:
Scan_Data.valid_filters[0][0]=valid_filters[0][0];
printf("\nValid_filters=%f",Scan_Data.valid_filters[0][0]);
?
4. Accessing a vector with operator[] is not safe
When you do:
Scan_Data.valid_filters[i][j]
You in fact do:
QFilterDataMap1D & v = Scan_Data.valid_filters[i]; // call QMap::operator[](double)
double d = v[j]; // call QVector::operator[](int)
The first one is safe, and create the entry if it doesn't exist. The second one is not safe, the jth element in you vector must already exist otherwise it would crash.
Solution
It seems you in fact want a 2D array of double (i.e., a matrix). To do this, use:
typedef QVector<double> QFilterDataMap1D;
typedef QVector<QFilterDataMap1D> QFilterDataMap2D;
Then, when you want to transfer one in another, simply use:
Scan_Data.valid_filters = valid_filters;
Or if you want to do it yourself:
Scan_Data.valid_filters.clear();
for(int i=0;i<n;i++)
{
Scan_Data.valid_filters << QFilterDataMap1D();
for(int j=0;j<m;j++)
{
Scan_Data.valid_filters[i] << valid_filters[i][j];
printf("\nValid_filters=%f",Scan_Data.valid_filters[i][j]);
}
}
If you want a 3D matrix, you would use:
typedef QVector<QFilterDataMap2D> QFilterDataMap3D;

foreach not working on list of QPair

Using Qt, I want this code to work:
QList<QPair<QString, QString>> list;
foreach (QPair<QString, QString> pair, list)
{
}
instead, I get the error:
'pair' : undeclared identifier
Using a typedef I can make it work, but this is not what I want (unless this is the only thing that works):
typedef QPair<QString, QString> MyPair;
QList<MyPair> list;
foreach (MyPair pair, list)
{
}
Can anyone explain why the first foreach doesn't compile?
it's not the foreach error. It's declaration error. You declared list like this:
QList<QPair<QString, QString>> list;
while it should this way:
QList<QPair<QString, QString> > list;
Just declare QPair outside of loop:
QPair<QString,QString> pair;
foreach(pair,list){
}
It is not possible to use template classes inside qt foreach statement which contains more than one template parameter, because comma separator conflicts with comma separator inside macros.
#define add( a, b ) (a + b)
template < typename T1, typename T2 >
struct DATA
{
static const T1 val1 = 1;
static const T2 val2 = 2;
};
// Usage
const int c = add( 1, 2 ); // OK
const int d = add( DATA< int, int >::val1 , DATA< int, int >::val2 ); // FAIL
because macros add will interpret "DATA< int" as first argument, and " int >::val1" as second, and so on.
Some explanation with above answer... if your compiler accept
QList<QPair<QString, QString>> list;
giving no error on such declaration, reasons for topic caster error is different and indeed has to do with a fact that declaration must be done outside of foreach() loop. That's explained in QT documentation.
regarding >> and > >... that's old story and latest GCC (so linux/mac) consider it to be a syntax mistake, because it's not conforming standard. >> in GCC manner is treated as operator with all follow-up errors..

How can I access variables in vector<struct> *obj?

How would I get my variables out of a vector?
I can't use the binary insertion operators or the equal operators.
Earlier, I declared a vector<someStruct> *vObj and allocated it, then returned the vObj
and called it in this function:
vector<sameStruct> firstFunc();
for (unsigned int x = 0; x < v->size(); x++)
{
v[x];
}
when I debug it, v[x] now has the full contents of the original vector, as it did before without the subscript/index.
But I don't think I've done anything to progress beyond that.
I just have about 4 variables inside my vector; when I debug it, it has the information that I need, but I can't get to it.
As it is written v is a pointer to a vector of structs.
When you index directly into v all you are doing is pointer arithmatic. v[x] is the vector of structs at position x (assuming that v is an array if it is just a single object at the end of the pointer then v[x] for x>0 is just garbage). This is because it is applying the [x] not to the vector pointed to by v but to the pointer v itself.
You need to dereference the pointer and then index into the vector using something like:
(*v)[x];
At this point you have a reference to the object at the xth position of the vector to get at its member functions / variables use:
(*v)[x].variable;
or
(*v)[x].memberfunction(parameters);
If you do not want to dereference the vector then access the element within it you might try something like:
v->at(x);
v->at(x).variable;
v->at(x).memberfunction;
This way you are accessing a member function of an object in exactly the same manner as when you called:
v->size();
I hope that this helps.
To use the [] operator to access elements you must do so on object, not a pointer to an object.
Try;
(*vec)[x];
E.g.
for (int i = 0; i < vec->size(); i++)
{
printf("Value at %d is %d\n", i, (*vec)[i]);
}
Note that when calling functions on a pointer you usually use the -> operator instead of the . operator, but you could easily do (*vec).some_func(); instead.
Operators such as [], --, ++ and so on can act both on objects and pointers. With objects they act as function calls, but on pointers they act as mathematical operations on the address.
For example;
pointer[nth];
*(pointer + nth);
Have exactly the same effect - they return the nth object from the start of the pointer. (note the location of the * in the second example, it's called after the offset is applied.
Two other tips;
You can also avoid the need to dereference like this by passing the vector as a reference, not a pointer. It's not always a suitable option but it does lead to cleaner code.
void my_func(std::vector<int>& vector)
{
// vector can then be used as a regular variable
}
If you're going to be passing vectors of a specific type to functions a lot then you can use a typedef both for clarity and to save on typing.
typedef std::vector<int> IntVector;
void my_func(IntVector& vector)
{
}

Resources