Notes on porting C to VB


The following are random suggestions and cautions you may find useful. The technique I use for porting is to paste the C code into the appropriate VB module and start correcting syntax errors. I generally use the template project and the ocx, as described in the pages on porting GLUT samples. If the C sample is small, it can be pasted into the main class of the VB app. If the C sample requires additional VB modules, I usually start them in a BAS module rather than a class because VB classes have restrictions on the kind of data which can be passed into and out of them. The BAS module can be treated conceptually as a class - you can implement properties and so forth - and you can convert it to a class later if necessary. Avoid using VB classes if possible unless the class is not time-critical - VB classes like Point or Color are a bad idea. Classes which wrap a C structure are sometimes OK, but if they are used in large arrays, you will see huge performance hits.


The steps in porting a C sample are somewhat as follows:

1. Gather the related C functions together in VB modules along with the variables they use. C code is often organized in ways which are inappropriate for VB.

2. Translate all the unproblematic variables and functions, leaving pointers and arrays for later.

3. Go through all the function bodies and translate all the simple code. During this phase you'll get a sense of where the problems are going to arise.

4. Once everything is translated except the pointer math on the arrays, you can make a final decision on how to represent this code in VB. By this time you should have a rough idea of the program's flow and how the data is to be used. Look over all the routines which use pointers to data before deciding how to translate it. Adjust the scoping of the data and functions and start trying to run it.

5. Keep trying to run it until it goes. Sometimes you'll get lucky and see something at this point, sometimes not. Now you have to walk through the code and find errors in the flow, math errors, and translation goofs. For gl code, you usually have to check the gl setup code and tweak it a bit to get the camera and lights right and all the gl state variables set.

6. Many C samples will run more or less correctly at this point. Often, however, there are small bugs or missing functionality, and you now get the chance to actually examine the gl code. All kinds of problems can turn up at this point, the following tips may help you find them.


Tips

Do the easy stuff first

The safest way to port C code is to make innocuous changes first. Replace '[' with '('. Fix the 'for' loops. Replace '&' and '|' with 'And' and 'Or'. Convert '#define's to 'Const's. Fix as many simple procedure definitions as you can. During this first pass you'll get an idea of the kinds of data structures being used and the naming convention being used. It's a good idea to rename the constants and macros. Then just follow your follow your nose. Pick easy and unproblematic stuff and do it first. Leave structs, arrays, and pointers for later.

Searching and Replacing

It's tempting to do wholesale search and replace operations on the C code, but you should not do this except in maybe a couple of cases ('[]', '&&', '||'). VB will coerce statements which are 'nearly' correct in some weird and unpredictable ways - including dropping numbers, punctuation, and operators. This can introduce subtle bugs which are very hard to find. Replacing can often be used, but you should never do a global replace - confine the operations to a single module and review each substitution.

Name collisions

C code will frequently contain variables, macros, and constants with the same spelling and different case. C is case sensitive, and VB is, well, quirky. VB will conflate these distict names to a single spelling if you do not watch very carefully what you are doing. If you make an error, VB will often find it when you try to run a project, but it is less work to search the C code for suspicious names than it is sort out names which have become confused. The best way to avoid this problem is to rename Consts and global variables with your own naming scheme.

ByVal/ByRef

By default, C procedures pass values 'ByVal', which is the opposite of VB's default. A procedure can modify an argument, and the effect will be different in C and VB. When you translate procedure parameters, use 'ByVal' with all simple parameters such as 'int x' or 'float dx'. Arguments which are passed as '*' or '&' are references, but it's often not possible to determine by examination how a procedure with reference arguments should be translated. A good idea is to translate all simple procedures early and wait until later to deal with procedures which recieve pointers.

Arrays

VB and C arrays are very different. Arrays are commonly used with structures and a pointer scheme of some kind which involves casting the datatype of the array elements, and these are the most difficult kinds of statements to port. Often, there simply is no way to port such code, and you will have to write an equivalent VB routine which may bear little resemblance to the C code. VB and C arrays are stored differently, so pointer arithmetic may or may not be equivalent to VB array indexes, usually not. One dimensional arrays of simple types usually can be rendered as a VB array of the same type, but 2 or more dimensional arrays should never be used in VB if the data is to be passed to an API function.

Math

C and VB do not handle math alike. C has an 'unsigned' which VB lacks, so the range of values can be a problem. Some other differences include:
Overflow - C will allow an 'int' to overflow without error, VB will not.
Rounding - you may sometimes need to specify the rounding behavior you want .
The C runtime library - this is one of VB's great weaknesses, that it cannot call C runtime library functions. You may need to write your own math routines. 'atan2' and 'arcsin' are commonly encountered.

Listing

Turn off the helper options while doing a port, they are useless and a hindrance.

GLUT

Don't call GLUT! Make sure you remove all GLUT calls when porting GLUT samples, because VB will load the dll and call the functions. Some GLUT calls are safe, like the 'WireXXX' and 'SolidXXX' functions, but almost none of the others should be used from VB and most will crash immediately if you haven't initialized GLUT. GLUT3.6 is not safe to use with VB, future or past versions may behave differently.

Pointers

In the simplest cases, a pointer to a variable can be translated to VB as the variable itself. That is, the C code is using the pointer to pass the variable to a function, so you can pass the variable itself ByRef.
Another simple case is when the pointer is used to iterate through an array. In this case, you set up a For loop to go through the array.
Anything else is likely to be a problem. If the pointer is used to cast data for API calls, you'll probably have to change the format you use for the data. This is about the worst thing you can discover when nearing completion of a port, as it can cause the whole thing to have to be redone. Watch for it and similar manipulations. Find these things early, before you decide how to translate the structs and arrays.
If the C code uses pointers to walk through arrays at various offsets, you can sometimes use an 'index' variable in a Do loop to accomplish the same thing.

Structs and Classes

A common problem is how to represent structs. The C code may allocate structs on demand, as when building linked lists. It's tempting to use a VB class in such cases, but look through all the code before you do so. C++ and VB classes are not at all equivalent. VB classes are designed for automation, and they are slow and restricted. Using a VB class may cause problems if the C code accesses the structs in a loop or passes data in the struct to an API function.

Debugging a port

The best way to debug a port is to compile a debug version of the C code and run your port and the C code side by side. Many of the problems in porting OpenGL code will come from math errors, and there simply isn't any way to isolate the problem without knowing what the values should be. Most simple OpenGL samples won't require this - you can walk through the VB code and find the error. But if you have to rewrite sections of code, you may need to compare the output of your code with that of the original.


Please send suggestions, bug reports, and such to:



Home Page   |  Related Sites