Lite-C for C/C++ programmers
Lite-C is very similar to the C language, but has some differences to make it easier approachable to beginners, and allow for a simpler compiler structure. In
most cases - when no particular C++ features, like STL, are used -
a C/C++ programm can be easily converted to lite-C. Examples
for this are the DirectX samples that come with the lite-C installation; they
are just slightly modified versions of the original C++ samples from the DirectX
9 SDK. The differences are listed here:
Pointers
Lite-C supports pointers, but does not require the '->' operator (although it's also supported and can be used if preferred). For addressing struct elements '.' can be used regardless if it's a pointer or not. If a function expects a pointer, lite-C automatically adds the '&' operator if required.
&&, ||
In C/C++, comparisons are early aborted when a && is encountered and the expression left of it evaluated to false, or when a || is encountered and the expression left of it evaluated to true. Lite-C does not early abort expressions. This requires a different syntax
when early abort is used for
checking the value of a struct pointer and its element in the same expression:
if((ptr != NULL) && (ptr->element == ..)) .. // C/C++
if(ptr != NULL) if(ptr->element == ..) .. // lite-C
Trinary operators
Lite-C does not support the comparison ? expression : expression; syntax,
so an if statement must be used instead.
x = (x<0 ? -1 : 1); // C/C++
if(x<0) x=-1; else x=1; // lite-C
Struct member alignment
In lite-C, struct members are always aligned to 1-byte boundaries. This ensures that a char really occupies only 1 byte in a lite-C defined struct, and a short only 2 bytes. For exchanging structs between lite-C and external languages, make sure to set the alignment accordingly (in VC++: Properties / C/C++ / Code Generation / Struct Member Alignment / 1 Byte). For speed reasons, it's recommended to put 4 chars or 2 shorts together in structs, which ensures alignment on 4-byte boundaries.
Struct and array initialization
In C/C++
structs can be initialized just like arrays, by giving a list of member values,
like VECTOR
myvector = { 0,0,0 };. This is only supported for global arrays
in lite-C. Local structs can not be initialized this way,
and local initialized arrays
automatically get the static property.
Lite-C initializes all global arrays and structs automatically to zero; local
arrays and structs are normally not initialized and thus contain random content.
You can use the zero() macro
(defined in acknex.h) for initalizing local structs to zero, and vec_zero() for
initializing vectors consisting of 3 vars:
VECTOR speed;
...
vec_zero(speed); // initializes the VECTOR "speed" to x=0,y=0,z=0
For migration and testing, lite-C can automatically initialize also all
local variables to zero with the PRAGMA_ZERO definition. If you
add
#define PRAGMA_ZERO // initialize variables
at the beginning of your script, all uninitialized local variables and
structs are set to zero. This slows down function calls a little, so it's
preferable not to to use PRAGMA_ZERO in your final version.
Struct copying
In C++, structs can be copied into each other with the '=' operator. In C or lite-C, use memcpy for copying structs:
// C++:
D3DXVector3 vecA, vecB;
...
vecA = vecB;
// lite-C:
D3DXVector3 vecA, vecB;
...
memcpy(vecA,vecB,sizeof(D3DXVector3));
Enums
Enums are not supported and can be replaced by defines:
enum RGB { RED=1; BLUE=2; GREEN=3 }; // C/C++
#define RED 1 // lite-C
#define BLUE 2
#define GREEN 3
Unions
Union members of the same type can be substituted
by a #define, and union members of different type can be treated as a
different members of the struct. Example:
typedef struct S_UNION {
int data1;
union { int data2; float data3; };
union { int data4; int data5; };
} S_UNION; // C/C++
typedef struct S_UNION {
int data1;
int data2;
float data3;
int data4;
} S_UNION; // lite-C
#define data5 data4
If the struct size must not change, or if for some reason the program requires
different variable types to occupy the same place in the struct, a special
conversion function can be used to convert the type of a variable without converting
the content:
typedef struct S_UNION {
int data1;
union { int data2; float data3; };
} S_UNION; // C/C++
...
S_UNION s_union;
s_union.data3 = 3.14;
typedef struct S_UNION {
int data1;
int data2;
} S_UNION; // lite-C
#define data3 data2
...
int union_int_float(float x) { return *((int*)&x); }
...
S_UNION s_union;
s_union.data3 = union_int_float(3.14);
Function pointers
In C/C++, function pointers are declared like this: int (*foo)(int
a, int b);.
In lite-C there's no difference between function prototypes and
function pointers: int foo(int a, int b);. See details under pointers.
Signed or unsigned variables
In lite-C, var, float, double, long and int variables are always signed, and pointers,
char and short are always unsigned, according to the normal way they are used.
The signed and unsigned variable modifiers are accepted, but have no effect.
The
include\litec.h file
contains definitions for all usual unsigned variables like DWORD or WORD that
are used in Windows functions. Therefore, using unsigned variables normally does not
cause any problems. However you need to take care when variables exceed
their range. For instance, subtracting 1 from (DWORD)0 results
in -1 under lite-C, but in 0xFFFFFFFF in standard C/C++, and would
produce different behavior in comparisons.
Prefix operators
Lite-C makes no difference between prefix and postfix operators; they always return the result of the operation. i++ is the same as ++i.
min, max
These often-used macros are replaced by simple functions: minv and maxv for var, and minf and maxf for float. The latter are no engine functions, but defined in include\windows.h.
sin, cos, tan, asin, acos, atan
The trigonometric functions are overloaded with their C-Script counterparts and use degrees instead of radians when called with a var or int argument. For enforcing the use of radians regardless of the argument type, use the (float) cast operator, as in sin((float)1).
printf
This old fashioned DOS output function won't work in normal windows programs, but is supported in lite-C pure mode for convenience reasons. It opens a message box (see printf). Make sure in its argument list to cast a var to (long) or (double), and convert STRING* to char* with the _chr function.
Main() function
In C/C++ the main() function terminates the program on return. In lite-C an explicit sys_exit(NULL) call (or clicking on the close icon) is used to terminate the program.
Also, in C the main() function does not open a window; in lite-C the main() function automatically opens a DirectX window in the first frame if this is not explicitely prevented by setting video_screen=0;.
Adding C library functions
Lite-C contains only a subset of all functions from the standard C/C++ libraries,
but you can add any function that you need as described under Using
the Windows API. Here's a brief instruction of how to add a API function
to lite-C:
- Find out in which Windows DLL the function is contained. C library function
are normally found in the mscvrt.dll. You can use a free DLL browser,
for instance the DLL Export Viewer from http://www.nirsoft.net,
for checking which DLL contains which function.
- Declare a function prototype in your source code. In most cases you can
directly copy the prototype from the .h header file that accompanies
the DLL, or from the documentation to the C library.
- Additionally to the prototype, either add a #define PRAGMA_API line
for initializing the function prototype, f.i. #define PRAGMA_API MessageBox;user32!MessageBoxA as
described in the API example. Or, for dynamic initalization, add a DefineApi call
in your main function.
If you need certain structs or variable types that are not yet contained in include\windows.h or
in the other standard include files, just add them from their original file
either into your script. If you think that a certain function, struct, or variable
type is often needed, suggest its inclusion into api.def on the Gamestudio
future forum rather than modifying api.def yourself. Self-modified files
are overwritten by lite-C updates.
Converting DirectX programs to lite-c
DirectX and other Windows SDKs use the Component Object Model (COM) as described
in the API chapter. For convenience, the Windows
interface definitions (like DECLARE_INTERFACE_ and STDMETHOD_)
are already contained in the lite-C include\d3d9.h file, and all basic
classes are already defined within. So it'is easy to add further interface classes
when they are needed. Just copy the class definition from the original d3d9.h or
other DirectX header file into it's lite-C counterpart, and add the inherited
methods (if any) from the parent class.
Although lite-C can use all DirectC classes and functions, it lacks some advanced C++ features such as operator overloading. Thus, for setting up, adding, subtracting, and multiplying D3DX vectors and matrices, the overloaded =, +, -, and * operators can't be used. Use the DirectX library functions, such as D3DXVec3Add etc. instead. For setting up vectors, the following convenience functions have been added to include\d3d9.h:
D3DXVECTOR3* D3DXVec3Set(D3DXVECTOR3 *pOut,float x,float y,float z,float w);
D3DXVECTOR3* D3DXVec3Set(D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pIn);
D3DXVECTOR3* D3DXVec3Set(D3DXVECTOR3 *pOut, CONST D3DXVECTOR4 *pIn);
D3DXVECTOR4* D3DXVec4Set(D3DXVECTOR4 *pOut,float x,float y,float z,float w);
D3DXVECTOR4* D3DXVec4Set(D3DXVECTOR4 *pOut, CONST D3DXVECTOR3 *pIn);
D3DXVECTOR4* D3DXVec4Set(D3DXVECTOR4 *pOut, CONST D3DXVECTOR4 *pIn);
When converting an engine VECTOR to a D3DXVECTOR3, make sure to swap the y and z coordinates. DirectX uses a different coordinate system.
With the above simple modifications, almost all DirectX examples that you can find on the Internet or in the DirectX SDK can easily be converted in a couple of hours and compiled under lite-c. You can find a converted version of the DirectX tutorial samples in the samples folder.
Compiling a lite-C script with other C++ compilers
As long as you don't use any special lite-C syntax, like global struct
initialization or pointer autodetection, you can normally compile a lite-C
legacy mode script with any other C++ compiler. Here's an example how to
compile the mandelbrot_legacy.c file with Visual Studio 2003:
- Start VC++ 2003. First you need to create an empty project. Select File
/ New / Project / Win32 Project. In the field below enter the project
name, like "Mandelbrot_Legacy", then click OK.
In the following dialog check Empty Project under Application
Settings, then
click Finish. VC++ will now create a project for you.
- Copy mandelbrot_legacy.c
into the project folder that VC++ has created, and rename it to .cpp (you're
using classes, so it's a C++ program). Add it to the project: In the VC++
Solution explorer, right click on Source
Files, and select Add / Add Existing Item / mandelbrot_legacy.cpp. That
should be the only file of the project.
- Because VC++ does not know about your
lite-C includes, you need to give the path to the litec.h file.
Edit the "#include <litec.h>" line,
and enter the path to litec.h in double quotes, like #include "C:\program
files\litec\include\litec.h".
- Compile it with
Build \ Build Solution, then run it with Debug
\ Start.
See also:
Pointers, Structs, Functions, Windows
API
► latest
version online