For PB's programmer wanting to make the big jump, there are several pitfalls you must be aware when porting from PB to C/C++.
- Always assign a default null value when you assign new parameters and new array of any kind.
In the case of array use:
memset(&AdressOfMyArray, 0, sizeof(MyArrayType) * NumberOfElementInArray)
PowerBASIC does this automatically under the hood, but not C/C++ that just allocates a new block of memory without clearing first its content.
There are only two cases where C/C++ assign default null value, when using globals and statics.
- PowerBASIC use "\" to perform integer division, and "/" for all other cases,
C/C++ uses only "/" and peforms optimization based on the type of the numbers being used.
For example in PB:
LOCAL sResult AS SINGLE, n1, n2 AS LONG
n1 = 71: n2 = 10
sResult = n1 / n2
sResult = 7.1 (that is what you espect).
Now the same code in C/C++:
float sResult = 0; long n1 = 71, n2 = 10;
sResult = n1 / n2;
sResult = 7 (this is not what you want)
To get the correct value, you must do this:
sResult = (float) (n1 / (float) n2);
and now sResult = 7.09999; (that is close to what you espect).
- Select case and switch are not working the same
In C/C++ you must add a break; at the end of each case or the code will go on to the next case.
This is because C/C++ doesn't allow to use the PB case construct below:
case %VK_LEFT, %VK_NUMPAD4:
but instead
case VK_LEFT:
case VK_NUMPAD4:
- Double check the IF syntax when doing comparisons
PowerBASIC uses the equal sign for either assignation or comparison example:
IF N1 = N2 THEN
but In C/C++ you must use two "==" signs to make the same test, example:
if (N1 == N2) { wcout << L"The two values are equals" << endl;
This would also apply to the OR(||) and AND(&&) comparisons when used in IF test construct.
Most of the time IntelliSense detects the error, but sometimes it fails to do so (and no compile error), then you can spend hours searching for a bug that doesn't exist. Thus always double check your IF syntax thoroughly, especially when you cut and paste from PB to C/C++.
- Performing string comparisons
PowerBASIC uses the equal sign or angle brackets for string comparison example:
IF String1$ = String2$ THEN
IF String1$ <> String2$ THEN
but In C/C++ you can't do this and expect the same result, because "==" or "!=" C/C++ would perform a pointer comparison instead of string comparison.
In order to get the good result you have to use _wcsicmp or strcmp, this way:
if ((_wcsimp(zClass, L"BUTTON") == 0) { // Check if the two strings are the same
The function returns an integer value indicating the relationship between the strings:
A zero value indicates that both strings are equal.
A value greater than zero indicates that the first character that does not match has a greater value in str1 than in str2;
And a value less than zero indicates the opposite.
Patrice,
My c++ is horrid but can't you assign a {0} instead of using memset?
James
I just found this on a forum:
Some quick testing indicates that Microsoft's x86 compiler generates different assembly if the initializer list is empty, compared to when it contains a zero. Maybe their ARM compiler does too. What happens if you do this?
byte a[10] = { };
Here's the assembly listing I got (with options /EHsc /FAs /O2 on Visual Studio 2008). Note that including a zero in the initializer list causes the compiler to use unaligned memory accesses to initialize the array, while the empty initializer list version and the memset() version both use aligned memory accesses:
; unsigned char a[10] = { };
xor eax, eax
mov DWORD PTR _a$[esp+40], eax
mov DWORD PTR _a$[esp+44], eax
mov WORD PTR _a$[esp+48], ax
; unsigned char b[10] = { 0 };
mov BYTE PTR _b$[esp+40], al
mov DWORD PTR _b$[esp+41], eax
mov DWORD PTR _b$[esp+45], eax
mov BYTE PTR _b$[esp+49], al
; unsigned char c[10];
; memset(c, 0, sizeof(c));
mov DWORD PTR _c$[esp+40], eax
mov DWORD PTR _c$[esp+44], eax
mov WORD PTR _c$[esp+48], ax
Yes, you can assign {0}
however when building dynamic array of structure, i would recommend to use memset.
Example of allocating a dynamic array:
ZSLIDESHOW* PlayList = new ZSLIDESHOW[resizeValue];
memset(PlayList, 0, sizeof(ZSLIDESHOW) * UB); // Fill up the whole new array with nulls (to mimic what PB's does)
//
...
//
delete [] PlayList <-- Never forget to release the memory allocated on the fly!
The Pitfall list has been updated with select/switch.
The pitfall list has been enhanced with:
QuoteDouble check the IF syntax when doing comparisons
Hello Patrice,
I am also using Visual C++ entirely for my projects now (well since beginning of 2012). With PB, I found myself hitting brick walls whilst debugging large projects and the Visual Studio debugger is so much more powerful.
Use the following statement to stop compilation when it finds common mistakes in your code:
// [C4101=unused locals, C4013=missing proc, C4700=uninit vars, C4701=potentially uninit vars, C4702=unreachable code, C4706=if assignment, C4715=control paths, C4716=no return value, 4047=indirection]
#pragma warning( error : 4700 4701 4702 4706 4047)
C4700, C4702 and C4716 are most useful!
I devised a common #include .H file with this and other basic stuff, such as typedefs and functions such as reset().
Also as char (PB: BYTE) is signed by default, this can create problems with legacy PB code.
Use the following:
#ifndef RC_INVOKED
#ifndef _CHAR_UNSIGNED
#error "COMPILATION ERROR: CHAR TYPE MUST BE UNSIGNED"
#endif
#endif
You need /J in CL compiler options to fix that one
The pitfall list has been enhanced with string comparisons.
Performing string comparisons
PowerBASIC uses the equal sign or angle brackets for string comparison example:
IF String1$ = String2$ THEN
IF String1$ <> String2$ THEN
but In C/C++ you can't do this and expect the same result, because "==" or "!=" C/C++ would perform a pointer comparison instead of string comparison.
In order to get the good result you have to use _wcsicmp or strcmp, this way:
if ((_wcsimp(zClass, L"BUTTON") == 0) { // Check if the two strings are the same
The function returns an integer value indicating the relationship between the strings:
A zero value indicates that both strings are equal.
A value greater than zero indicates that the first character that does not match has a greater value in str1 than in str2;
And a value less than zero indicates the opposite.
Patrice,
It appears you are showing a direct translation from PB to c++ ?
I prefer to use c++ compare.
James
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string s1;
string s2;
wstring ws1;
wstring ws2;
s1="James";
s2="Fuller";
ws1= L"James";
ws2= L"Fuller";
if(s1.compare("James")==0 )
{
cout<<"YES"<<endl;
}
else
{
cout<<"NO"<<endl;
}
if(ws1.compare(L"James")==0 )
{
cout<<"YES"<<endl;
}
else
{
cout<<"NO"<<endl;
}
system("pause");
return 0;
}
James--
QuoteIt appears you are showing a direct translation from PB to c++ ?
No, i am showing the use of
_wcsicmp or
strcmp, when using
CHAR or
WCHAR comparison :)
but
compare is fine when using
string or
wstring.
Patrice,
Another item is scope of variables. j is only visible within the if scope here.
int i = 1;
if(i) == 1 {
int j = 7;
}
// this will get you an error
std::cout << j << endl
Even more fun is that you can do something like this:
void foobar()
{
int i, x;
// a lot of code goes here
for (i = x = 0; i < 10; i++)
{
// even more code goes here
if (i % 5)
{
int x, y, z;
// put even more code here
x += i; // doing something with x
// and some more code here
}
}
// and if the function isn't huge enough, add some more code
// to make sure the kitchen sink is included
}
Now, a good compiler that's set to issue fairly strict warnings should complain about the fact that you're using an uninitialized 'x' ... and you may scratch your head for a second thinking, "hey, I've initialized 'x' at the top of the for loop there!" Then the proverbial light bulb will come on over your head.