[solved] Are qt data types (qint8, qint16) better ?
-
Since I discovered qt I loved that it would give me some basic data types that have a fixed bit size; I think they are better suited in cross-platform projects because you'll never have a surprise where data types might have a different bit size. So in my projects I'm now using almost everywhere the qt defined data types (qint8, qint16 ect...).
Do you guys do the same? Would you suggest me to keep using them or not, and why?
-
Using Qt types, like qint8 and qint16, makes your code more portable, indeed. That's because you cannot rely on "short" to always be a 16-Bit type, but qint16 will always be defined as 16-Bit. On the other hand, using the Qt types makes your code more dependent on Qt, which makes reusing that code in other contexts more difficult. With the types defined in <stdint.h>, like int8_t and int16_t, the C Standard already provides its own "portable" types.
-
I would argue that using these types makes your code less portable. First of all, not all of them need to exist in a given implementation (it is e.g. perfectly conceivable that there is no 16 bit data type). Secondly, integer promotion rules can lead to subtle problems when using integers with specific width:
uint32_t x = std::numeric_limits<uint32_t>::max();
uint32_t y = x * x; // undefined behavior if int is 64 bit wide, well defined if int is 32 bits (and therefore uint32_t is unsigned int) -
Qt guarantees that all those Qt specific types are available and defined with the proper size on all supported platforms. So how does it make the code less portable? If I use, for example, "qint16", then I do this because I definitely want/require a 16-Bit type here. If I did not use "qint16", I could use a native type like "short", which happens to be 16-Bit on most modern platforms. But I cannot be sure that is the case on all platforms! By using "qint16", I know that Qt will take care of defining that type correctly on every supported platform. That makes the code more portable. When dealing with native types, I have to verify (and maybe modify) the code manually on every platform...
[quote author="mmoll" date="1385635130"]
uint32_t x = std::numeric_limits<uint32_t>::max();
uint32_t y = x * x; // undefined behavior if int is 64 bit wide, well defined if int is 32 bits (and therefore uint32_t is unsigned int)[/quote]Can you elaborate on that example? uint32_t is a fixed size type, guaranteed to be exactly 32-Bit (unsigned) on every platform. So how does the size of "int", which is platform-specific, matter? x is uint32_t (exactly 32-Bit, unsigned), so if you set x to the maximum value that can be represented in uint32_t (2^32 - 1) and multiply it by itself, the result certainly will be outside the range of an uint32_t. And since y is uint32_t as well, the value will be truncated (wrap round) to 32-Bit and thus be "1". On any platform that has <stdint.h>.
-
For real portability it might even be better to use custom made typedefs for you variables.
like so:
@
// When using Qt:
typedef quint8 GB1; // _bit
typedef qint8 GS8; // signed char
// Other compiler
typedef bool GB1;
typedef char GB8
@
Then using your own defines you can't go wrong, but if you stay within Qt supported compiler environment it is safe to use only Qt defines. In our company we only use our own defines to make it portable from Qt-debugger environment to Tricore embedded GCC compiler. -
IMO, when using your own type definitions you will have to check and, if necessary, modify those definitions on every single platform - which is the exact opposite of "portable" code. To make your code "portable", you need a layer of abstraction, as is provided by Qt or by <stdint.h>.
Of course you could use a whole lot of #ifdef magic to make your custom definitions work an many platforms, but then you end up re-inventing <stdint.h> or <qglobal.h>. This contradicts the idea of using libraries - write once and reuse, instead of re-inventing the wheel again and again ;-)
-
[quote author="MuldeR" date="1385639415"]Qt guarantees that all those Qt specific types are available and defined with the proper size on all supported platforms.[/quote]
You're right, I wasn't certain about that.
Of course that means that using Qt in general, rather than Qt's qintN types, limits portability ;-)
So from here on, let's consider the more general case of the stdint types (which are not guaranteed to exist), without using Qt.[quote author="MuldeR" date="1385639415"]So how does it make the code less portable? If I use, for example, "qint16", then I do this because I definitely want/require a 16-Bit type here. If I did not use "qint16", I could use a native type like "short", which happens to be 16-Bit on most modern platforms. But I cannot be sure that is the case on all platforms! By using "qint16", I know that Qt will take care of defining that type correctly on every supported platform. That makes the code more portable. When dealing with native types, I have to verify (and maybe modify) the code manually on every platform...[/quote]
Ok, my fault for not clarifying what the baseline was. Of course, just taking a short and assuming it is 16 bits is the least portable thing you can do. The most portable is to assume nothing about the widths of data types beyond what is guaranteed by the standard.[quote author="MuldeR" date="1385639415"][quote author="mmoll" date="1385635130"]
uint32_t x = std::numeric_limits<uint32_t>::max();
uint32_t y = x * x; // undefined behavior if int is 64 bit wide, well defined if int is 32 bits (and therefore uint32_t is unsigned int)[/quote]Can you elaborate on that example? uint32_t is a fixed size type, guaranteed to be exactly 32-Bit (unsigned) on every platform.
[/quote]
every platform on which it exists.
[quote author="MuldeR" date="1385639415"] So how does the size of "int", which is platform-specific, matter? x is uint32_t (exactly 32-Bit, unsigned), so if you set x to the maximum value that can be represented in uint32_t (2^32 - 1) and multiply it by itself, the result certainly will be outside the range of an uint32_t. And since y is uint32_t as well, the value will be truncated (wrap round) to 32-Bit and thus be "1". On any platform that has <stdint.h>.[/quote]
Not quite. The problem is integer promotion. In almost any arithmetic expression, the operands are first subject to integer promotion, which means that any type "smaller" than int is converted to either int (if any value of the small type can be represented by int) or unsigned int (otherwise). So if your int is larger than a uint32_t, the uint32_t's are converted to (signed) ints! That means that overflow of any computation is undefined. The type of y in the example above isn't relevant, as the assignment is performed only after the multiplication is computed.The same problem will also change for example which overloaded function will be called in say:
void f(int32_t x);
void f(uint32_t x);
void f(int64_t x);
void f(uint64_t x);uint32_t x = 10; // edited, was int32_t
f(x); // will always call void f(uint32_t)
f(x*x); // will call void f(uint32_t) on platforms where int is 32 bits, f(int64_t x) on platforms where int is 64 bits.It doesn't stop there. The fundamental "problem" is that integer promotion rules are formulated in terms of the fundamental types "short", "int", "long", etc.
I was concerned because T3STY wants to use fixed size types "almost everywhere". My personal opinion is that you should use them sparsely and wisely, and use "short", "int", etc. for computations wherever possible. Or maybe just never use short.
-
Thank you a lot for your replies guys!
[quote author="Jeroentje@home" date="1385640893"]For real portability it might even be better to use custom made typedefs for you variables.
[/quote]
Sorry mate but I have to totally disagree with that. Even if I would be using pure C++, it would meant that I would have to redefine even the basic data types defined by C++ (char, int, float, double ect...) to something of my own... which, as MuldeR said, would lead you to reinventing the wheel and contraddicting the idea of using libraries.[quote author="MuldeR" date="1385605502"] On the other hand, using the Qt types makes your code more dependent on Qt, which makes reusing that code in other contexts more difficult.[/quote]
I agree that using Qt data types makes it more Qt dependent; but on the other hand, you wouldn't be using qt data types at all if you weren't using the Qt framework for your project in first place. So either I'm wrong or I didn't really catch your idea on this... could you make an example where using qt data types makes it more difficult to reuse the code in other contexts ?EDIT
[quote author="mmoll" date="1385656595"]
I was concerned because T3STY wants to use fixed size types "almost everywhere". My personal opinion is that you should use them sparsely and wisely, and use "short", "int", etc. for computations wherever possible. Or maybe just never use short.[/quote]
I read your explanation above and that seems to make sense. So you're basically telling me that whenever possible I should avoid using qt defined types in favor of C++ standard types, and only use qt data types when a fixed bit size data type is needed. Is that right? -
[quote author="T3STY" date="1385656956"][quote author="MuldeR" date="1385605502"] On the other hand, using the Qt types makes your code more dependent on Qt, which makes reusing that code in other contexts more difficult.[/quote]
I agree that using Qt data types makes it more Qt dependent; but on the other hand, you wouldn't be using qt data types at all if you weren't using the Qt framework for your project in first place. So either I'm wrong or I didn't really catch your idea on this... could you make an example where using qt data types makes it more difficult to reuse the code in other contexts ?[/quote]Well, maybe even if your GUI code is based on Qt, you may still have some "backend" code that is (or should be) mostly independent from the GUI code. That part of the code you may which to reuse in other (non-Qt) projects. Some projects even support building with different toolkits, such as Qt, GTK+ and WxWidgets, while the "backend" code is always the same. Now, if you used a lot of Qt specific types in places where you could have used standard C types just as well, such things can become more nasty...