• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

PB to C/C++ pitfalls

Started by Patrice Terrier, May 11, 2013, 03:01:07 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Patrice Terrier

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 Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

James C. Fuller

Patrice,
  My c++ is horrid but can't you assign a {0} instead of using memset?

James

James C. Fuller

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

Patrice Terrier

#3
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!
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Patrice Terrier

The Pitfall list has been updated with select/switch.
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Patrice Terrier

The pitfall list has been enhanced with:
QuoteDouble check the IF syntax when doing comparisons
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Kev Peel

#6
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().

Kev Peel

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

Patrice Terrier

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 Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

James C. Fuller

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;
}



Patrice Terrier

#10
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 Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

James C. Fuller

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

Mike Stefanik

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.
Mike Stefanik
sockettools.com