Bay Six Software Forum Index Bay Six Software
Beyond the Basics
 FAQFAQ   SearchSearch   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Converting C types to LB types More Info
By Brent D. Thorn


One of the largest obstacles faced by novices to the Windows API is the determination of which data types to use. Microsoft provides good documentation of function calls, their parameters, and data structures; however, almost all of these are written in the C language. If you are highly proficient in C, converting these types to Liberty BASIC is not hard at all. This article is for those who are not so proficient.

Like Liberty BASIC, C has a small base set of "primitive" data types with well-defined sizes and value ranges. Unlike LB, C has facilities to define (virtual) aliases of predefined data types. C programmers do this for "type safety" -- the value passed to a function must have the same type as defined by the function's declaration, otherwise the program may not compile, even if the types are basically the same. The purpose of this is to prevent mistakes, but it makes converting C code to LB more difficult.

Basics of Reading C

Liberty BASIC uses the form <variable> AS <type> for CALLDLL and STRUCT statements.

Whereas, C declarations generally use the form <type> <variable>.
returnType functionName(paramType parameter);

However, the variable name is optional in a function's declaration. In documentation, this form is usually seen only in functions that take no parameters.
returnType functionName(VOID);

C Structs

Structs in C can look very similar to LB's struct statement.
/* C */
struct POINT { long x; long y; };
' LB
struct POINT, x as long, y as long

The most significant difference between the two is that in C POINT is a type, while in LB it is a variable. C programs can declare and use a virtually unlimited number of POINTs, just by declaring them.
/* C declaration of 5 different POINT variables */
POINT pt1, pt2, pt3, pt4, pt5;

Small Arrays of Primitive Types

When a DLL call requires an array to be passed, LB shows a major limitation. Where C simply defines the array with square brackets ([ ]) and passes the address by omiting them, LB must use a struct. This is the case even though LB otherwise supports arrays.

Most often, the C declaration will not explicitly state its requirement of an array parameter, and cannot specify an array of a defined number of elements. Instead, it will want a pointer to the array. The following example function takes an array of longs.
void DoTheArrayThing(long *arr, int size);

If you want to pass an array of four elements to this function, which also takes a second parameter to tell it the size of the array, you must do the following in LB.
struct arr, e1 as long, e2 as long, e3 as long, e4 as long
arr.e1.struct = 123
' etc.
calldll #dll, "DoTheArrayThing", arr as struct, 4 as long, r as void

Obviously, for large arrays this can get out of hand quickly.

Large Arrays of Primitive Types

For large arrays, the solution has been to use a string to store the elements. However, one cannot just use the STR$() function to convert numeric types to a string representation because this is an ASCII representation and a binary representation is what is needed.

Fortunately, there exists a set of LB native functions for doing such conversions right on this website. See MKD$(), MKI$(), MKL$(), and MKS$(). The "MK" is short for "make" and the final letter is short for the type: "D" for "double", "I" for "integer" ("short" in C), "L" for "long", and "S" for "single" ("float" in C).

If you want to fill an array of longs with the first 40 numbers of the Fibonacci series:
a = 1 : b = 1
arr$ = MKL$(1)
for i = 2 to 40
    arr$ = arr$ + MKL$(b)
    c = b : b = a + b : a = c

Structs within Structs

C's structs may be defined with almost any type of element, including other structs. Recursion is not permitted, however.
/* C definition of a rectangle using POINTs */
struct RECT { POINT ul; POINT lr; };

Many LB coders upon seeing such a construct for the first time, analogize it to passing a struct to a DLL call and write something like the following.
' Incorrect translation of a struct within a struct
struct ul, x as long, y as long
struct lr, x as long, y as long
struct RECT, ul as struct, lr as struct

Why this does not work is that in all cases "as struct" defines a pointer to a memory location, not a copy of the named struct. If you were to print the len() of each of these structs, this fact would be evident.
print len(ul.struct) ' 8
print len(lr.struct) ' 8
print len(RECT.struct) ' 8

Unfortunately, the solution requires duplication of code.
struct RECT, ul.x as long, ul.y as long, lr.x as long, lr.y as long

RECT.ul.x.struct = 50 ' set upper-left x
RECT.ul.y.struct = 30 ' set upper-left y


Under construction

A union, at first glance, looks almost identical to a struct; however, they are very different.

A struct says, "I want this and that and the other thing." A union says, "I want this or that or the other thing, but not all at the same time."

One way to visualize these: a struct places each element side-by-side, while a union stacks all of the elements on top of each other.

Powered by phpBB © 2001, 2005 phpBB Group