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: