foreach not working on list of QPair - qt

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..

Related

QStringList split() function and simplifyWhiteSpace() function

I am using Qt5 and errors exist from this line of codes after running sample project I found on the internet.
QFile f( "world.txt" );
if( f.open( QIODevice::ReadOnly ) ) {
QTextStream ts( &f );
Vertex v[3];
int vcount = 0;
bool allok, ok;
while( !ts.atEnd() )
{
QStringList line = QString::split( " ",ts.readLine().simplifyWhiteSpace() );
Errors are:
split is not a member of QStringList
simplifyWhiteSpace is not a member of QString
I don't know how to convert the line to work on Qt5.
Both QStringList::split() and QString::simplifyWhitespace() were functions in Qt3, and have been renamed or moved for Qt5 (which you are using according to your tags).
For QStringList::split(), the documentation says:
Use QString::split(sep, QString::SkipEmptyParts) or QString::split(sep, QString::KeepEmptyParts) instead.
Be aware that the QString::split()'s return value is a QStringList that always contains at least one element, even if str is empty.
You already changed this in your edit, so you are left with QString::simplifyWhitespace(), where the documentation says:
QString QString::simplifyWhiteSpace () const
Use simplified() instead.

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

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.

String ^ MyString = gcnew String("abcd");

I was reading the c++'s foreach syntax on MSDN:
// for_each_string1.cpp
// compile with: /ZW
#include <stdio.h>
using namespace Platform;
ref struct MyClass {
property String^ MyStringProperty;
};
int main() {
String^ MyString = ref new String("abcd");
for each ( char c in MyString )
wprintf("%c", c);
wprintf("/n");
MyClass^ x = ref new MyClass();
x->MyStringProperty = "Testing";
for each( char c in x->MyStringProperty )
wprintf("%c", c);
}
I tried to find what the "^" means on google but I couldn't find anything (or my query wasn't correct)
What does it mean? Is it as a "*"? Is it as a "&"?
This syntax also applies to C#. Do they mean the same thing in both languages?
Piece of C# code:
using namespace System;
int main(){
array<int>^ arr = gcnew array<int>{0,1,2,5,7,8,11};
int even=0, odd=0;
for each (int i in arr) {
if (i%2 == 0)
even++;
else
odd++;
}
Console::WriteLine(“Found {0} Odd Numbers, and {1} Even Numbers.”,
odd, even);
}
The "^" denotes a managed reference, which is used for managed types. It's like a pointer, but for managed types. The syntax doesn't exist in C#. In C#, the equivalent is just a variable of a reference type (as opposed to a value type), or a boxed value type.
As others have said, this is C++/CLI syntax which means you have to compile with the /clr option. C++/CLI is basically C++ with features of C# (or more generally, .NET).
https://learn.microsoft.com/en-us/cpp/extensions/handle-to-object-operator-hat-cpp-component-extensions?view=vs-2019 hope this link can help you.
btw,^ is a special character and ignored by google search engine so you can not search that.

Nested QMap and QList won't let me append/push_back

I am trying to utilize a nested QList:
QMap<int, QMap<QString, QList<int> > > teamGames;
for (int team1 = 1; team1 <= TOTAL_TEAMS; ++team1) {
QMap<QString,QList<int>> games;
teamGames[team1]=games;
QList<int> home;
QList<int> away;
games["home"] = home;
games["away"] = away;
}
teamGames.value(1).value("home").push_back(1);
When I compile I get:
1>.\main.cpp(154) : error C2662: 'QList::push_back' : cannot convert 'this' pointer from 'const QList' to 'QList &'
I'm sure its something simple that I'm overlooking, or maybe there is a simpler solution that's eluding me. Any help greatly appreciated.
As you can see here QMap::value(const Key & key) const; returns a const T, which means you can not modify what you get. Even if you could you would modify a copy of the value you put into the map. What you need is T& QMap::operator[](const Key& key) which returns the value associated with the key as a modifiable reference. So call
((teamGames[1])["home"]).push_back(1);

Pointer won't return with assigned address

I'm using Qt Creator 4.5 with GCC 4.3 and I'm having the following problem that I am not sure is Qt or C++ related: I call a function with a char * as an input parameter. Inside that function I make a dynamic allocation and I assign the address to the char *. The problem is when the function returns it does not point to this address anymore.
bool FPSengine::putData (char CommandByte , int Index)
{
char *msgByte;
structSize=putDatagrams(CommandByte, Index, msgByte);
}
int FPSengine::putDatagrams (char CommandByte, int Index, char *msgByte)
{
int theSize;
switch ( CommandByte ) {
case (CHANGE_CONFIGURATION): {
theSize=sizeof(MsnConfigType);
msgByte=new char[theSize];
union MConfigUnion {
char cByte[sizeof(MsnConfigType)];
MsnConfigType m;
};
MConfigUnion * msnConfig=(MConfigUnion*)msgByte;
...Do some assignments. I verify and everything is OK.
}
}
return theSize;
}
When I return the pointer it contains a completely different address than the one assigned in putDatagrams(). Why?
...
Ok thx I understand my mistake(rookie mistake :( ). When sending a pointer as an input parameter to the function you send the address of your data but not the address of your pointer so you cant make the pointer point somewhere else...it is actually a local copy like Index. The only case the data would of been returned succesfully with the use of a char * is by allocating the memory before the function call:
bool FPSengine::putData (char CommandByte , int Index)
{
char *msgByte;
msgByte=new char[sizeof(MsnConfigType)];
structSize=putDatagrams(CommandByte, Index, msgByte);
}
int FPSengine::putDatagrams (char CommandByte, int Index, char *msgByte)
{
int theSize;
switch ( CommandByte ) {
case (CHANGE_CONFIGURATION): {
theSize=sizeof(MsnConfigType);
union MConfigUnion {
char cByte[sizeof(MsnConfigType)];
MsnConfigType m;
};
MConfigUnion * msnConfig=(MConfigUnion*)msgByte;
...Do some assignments. I verify and everything is OK.
}
}
return theSize;
}
There are two ways. The pass-by-value way (C style):
int FPSengine::putDatagrams (char CommandByte, int Index, char **msgByte)
Note the second * for msgByte. Then inside of putDatagrams(), do:
*msgByte = new char[theSize];
In fact, anywhere in that function where you currently have msgByte, use *msgByte. When calling putDatagrams(), do:
structSize=putDatagrams(CommandByte, Index, &msgByte);
And the second way, since you're in C++, you could use pass-by-reference. Just change the signature of putDatagrams() to:
int FPSengine::putDatagrams (char CommandByte, int Index, char * &msgByte)
And you should be good. In this case, you shouldn't need to modify the caller or anything inside of your putDatagrams() routine.
Well, yes. Everything in C++ is, by default, passed by value. Parameters in the call putDatagrams(a, b, c) are sent by value - you wouldn't expect assigning to index in the code to change the value of b at the call site. Your msgByte=new char[theSize]; is just assigning to the local variable msgByte, overwriting the value passed in.
If you want to change a passed parameter such that the call site variable changes, you'll need to either pass by reference, or (in this case) pass a "pointer to a pointer` (and deference away the first pointer, assigning to the actual pointer).

Resources