Issue with QLatin1String and nested QHash implementations.
Hi folks. I am going to try this again because my post has gone unanswered by anyone but me and it turns out that the issue is PROBABLY different than I first posted. I have several questions regarding this issue so I will number (and bold) them as I ask them.
I have a series of "Info" classes that get populated by reading xml files. These infos MUST be found quickly by name so I am using a QHash of <QLatin1String, infoPointer*>
1. My first question, is QLatin1String a good choice for a string based key? (I would think it is faster than QString since each character is only one byte). Also... I am now getting an error in compilation that says QLatin1String has no default constructor so this is the second reason I am asking this question (this might be due to another issue. See below).
In the past, using the QLatin1String has worked. However, with the addition of a new, currently experimental convention, QLatin1String seems to be causing a problem.
In addition to having a fast lookup, I also need to have multiple sets of these info classes. (Multiple "mods" can be loaded at the same time). I only need to deal with one set of these classes at a time, but each set must also be put in either a QMap or QHash so that I can switch them out quickly when necessary. So I created a set of classes that actually inherit from QHash in order to create a QHash of a QHash. (You can't normally do this: QHash<QLatin1String, QHash<QLatin1String, infoPointer*>*> so I created this workaround below).
The info lookup class:
@class APPCORE_EXPORT InfoCollection : public QHash<QLatin1String, BaseModInfo*>@
The info class container:
@class APPCORE_EXPORT ModInfoCollection : public QHash<QLatin1String, InfoCollection*>@
2. Notice that the second class's value class is the class that inherits QHash. Is this an ok approach to use? Or is this a bad idea?
The InfoCollection class is causing my current issue. Any file that (directly or indirectly) includes the header for this class WILL NOT compile. (Both of these classes are in that header). It complains that QLatin1String does not have a default constructor. What I don't understand is that I didn't need one before but for some reason, when I use this nest pattern it does. Here is the error I get (for each of the 10 files where including the file in question is necessary):
@error C2512: 'QLatin1String::QLatin1String' : no appropriate default constructor available D:\Developing\Projects\IMT\API\3RD_PARTY\Qt\126.96.36.199\5.3\msvc2012_opengl\include\QtCore\qhash.h 706@
In case it helps understand the nature of the issue, here is the function including line 706 from qhash:
@template <class Key, class T>
Q_OUTOFLINE_TEMPLATE const Key QHash<Key, T>::key(const T &avalue) const
return key(avalue, Key()); // LINE 706
3. Before my addition of the nested Hash tables, this line did not cause an issue even though I was using QLatin1String as a key. So I am curious as to why.
Also... here is some sample output from compilation that is correlated with the error in case it helps:
1>D:\Developing\Projects\IMT\API\3RD_PARTY\Qt\188.8.131.52\5.3\msvc2012_opengl\include\QtCore/qhash.h(706): error C2512: 'QLatin1String::QLatin1String' : no appropriate default constructor available
1> D:\Developing\Projects\IMT\API\3RD_PARTY\Qt\184.108.40.206\5.3\msvc2012_opengl\include\QtCore/qhash.h(705) : while compiling class template member function 'const QLatin1String QHash<Key,T>::key(const T &) const'
1> T=AppCore::BaseModInfo *
1> d:\developing\projects\imt\rosweld\trunk\source\MyApp\Appcore\IAppInfoClass.h(115) : see reference to class template instantiation 'QHash<Key,T>' being compiled
1> T=AppCore::BaseModInfo *
- Why can't you create that container ?
@QHash<KeyType, QHash<KeyType, ValueType> >@
AFAIK, QLatin1String use cases doesn't fit your need.
It can't create a default key because QLatin1String doesn't provide a default constructor (nor should it)
Same as 2
For #1, I tried it before and got a intellisense error (in Visual Studio). So I looked it up online and I took the advise I found there. I suppose it is possible but perhaps Visual Studio doesn't like it, or at least it didn't like the key/QHash combo I was using. I will have to figure that part out. I actually do like inheriting from QHash though because it allows me to do some other necessary bookkeeping tasks in a less convoluted way.
For your answers to 2 and 3: You said (I am not able to get quote to work for this quote for some reason...)
"It can’t create a default key because QLatin1String doesn’t provide a default constructor (nor should it)"
If that is the case, why did it work fine when I wasn't using a nested class? Why is a default key needed in one case but not the other. In my "earlier post":http://qt-project.org/forums/viewthread/49071/ I explained that it worked fine before. I was able to use QLatin1String as a key. Why?
Also... Why would QLatin1STring not fit my use case (other than a lack of a default constructor)? I can't find a qHash override method (for finding a hash code) in the list provided that handles multiiple character input for char* (a single byte character string) other than QByteArray& which would require conversion and create other issues. So is that my only choice? What else could I use? (I know I can use QString but I would prefer a single byte character). I am willing to use another key type if it fits my needs (which is to base the hash code off a string of single byte wide characters).
For 1 did you wrote the > > with the space between ?
When you said it was working, were you actively using that class or was it just declared ?
Because QLatin1String is meant to be used for "String Literals":en.wikipedia.org/wiki/String_literal and a hash key is not a string literal.
QString sounds like what you need, if you think not can you explain what exactly will get put in that key ?
I don't remember if I used a space or not.
I did not add the nested class until after the reinstall (a week or so ago). So before I did not use a nested class at all. But I did have several QHash<QLatin1String, someValueClass> instances (such as the one I put in my first post in the other thread). All of them worked fine.
I know what a hash key is. I have built my own hashing methods before. My point is that a hash method exists for QLatin1String so why has this been done if QLatin1String is a bad choice? From the QHash 5.3 documentation:
@uint qHash(QLatin1String key, uint seed = 0)@
Seeing the above hash method is why I chose QLatin1String in the first place. I would really prefer NOT to use QString for one reason. The key string is always based on an object name defined in xml. Even when other languages are used in the future, the actual names of objects will only need the standard ascii set (one byte wide characters). So I would prefer a hash key that is calculated using a string of single byte wide characters.
Because you can create a QHash in code using string literals as key which is fine AFAIK but your use case doesn't involve string literals you are creating these string dynamically so you would have to also ensure that the original data doesn't get deleted since QLatin1String doesn't make any copy and requires that the original data lives as long as the QLatin1String object itself.
What issues would you have with QByteArray ?
As for the nesting problem: the type used as value must have a default constructor (more about "here":http://qt-project.org/doc/qt-5/containers.html). If you try to get a value using a key not found in the hash you'll get a default constructed value which is not possible using QLatin1String as key
Here is my problem with QByteArray. From the documentation:
[quote]QByteArray makes a deep copy of the string data.[/quote]
I need these info classes because my application is module based and each module is potentially vastly different. The only thing standardized about them is how they define their components (by way of the info files) and how those components are organized.
However, each module will be able to make use of its own infos using string literals (because a module designer will know the names of the infos they use). In addition, any info that gets converted into an object managed by the object manager must have a char* that can be used without duplication because I have to manage two different string types from two different libraries (Qt and Irrlicht) and Irrlicht does not have a wrapper class like Qt does. I was relying on QLatin1String because that allowed me to wrap irrlicht's pointers to their object names to use as a key for lookup without duplicating the string.
I don't want to waste processor time by duplicating a string every time I need to look up an object from code. Instead, I would like to directly control if and when a string gets duplicated (so that it happens only when necessary).
[quote author="SGaist" date="1414744311"]If you try to get a value using a key not found in the hash you'll get a default constructed value which is not possible using QLatin1String as key [/quote]
That makes sense. Is their a way I could somehow supply this default value?
Then fromRawData looks like what you need, no ?
Yeah! Thanks. That WILL work. I don't always remember to look for static "constructors."
On second thought, No it won't. I was in a hurry before and didn't pay close attention. The problem is that it still requires unnecessary processing in order to calculate the size of the array a priori so that the QByteArray knows how long the array is (since a string isn't guaranteed). A normal hash code calculator for a string doesn't need to know how long the string is because it just keeps operating on the hash code until it encounters the null terminator.
I am beginning to think I will just have to create my own hash table class and use the qHash(QLatin1String) method for calculating the hash value.
Do I have any other options?