current stable:
0.99.6
unstable:
cvs (0.99.7)
General
  Home / News
  About
  Contact
  The Team

Obtaining
  Download
  Source Tarball
  CVS Web View
  Misc. Files

Documentation
  Introduction
The Manual
  Download [html]
  Download [pdf]
  View Online
API
  Download [html]
  View Online
Resources
  Script Examples

Developer
  Introduction
Developer Guide
  Download [html]
  Download [pdf]
  View Online
Ferite C API
  Download [html]
  View Online




Open Source Approved

SourceForge Logo
KwMap.net - browse the Keyword Map of ferite.org

[previous] Builder[up][toc]Without Builder [next]


Ferite-C File Contents

Ferite-c files are very similar to basic modules. In fact you can quite easily run the builder on a basic module. You just would end up with a lot of source files that didn't have much content. In order to get some content into those files, we need to tell the builder what parts of our module are written in C, instead of ferite script. To do this, there are several new sections and keywords that we can place within our ferite-c file.

    uses "modulename.lib"
    			

One of the most important pieces of a ferite-c file, is a uses statement at the top that tells ferite at runtime to load the shared object file for the native module.

When you compile the files that builder creates into a shared object, ferite has no way of knowing the resulting file's name. Usually, people will compile it into a file called modulename.so, where modulename is the name of the module. However this is not required. You could quite easily compile a module from source obtained by building bob.fec, and call it jimmy.so.

The solution is to explicitly tell ferite to load a shared object by name. This is done with a special case of the uses statement. The syntax is much like the normal uses statement, only you place a .lib extension on the name of the module that is to be imported. This is such that ferite can know to load the native library for that platform without forcing the programmer to take into account specifics of that platform.

    uses "bob.lib";
    			

This will tell ferite to look in the native module path for a file called bob.so on Linux and bob.dylib on Mac OS X, and to import it. This type of a uses statement can also be used within a regular ferite script to load a native only module. (How to create native only modules is covered later.)

module-header

The module-header section is where you will place any #include statements, or #define statements, or anything else that you expect your native code will need. The syntax for creating a module-header in a ferite-c file is much like defining a global section in a regular script. The code that is declared within the module-header is availible in all generated C files.

For example:

    module-header{
    	...your headers go here...
    }
    				

Anything you place within the module-header section will be placed in the modulename_header.h file when builder parses the ferite-c file. This header is then included in every C source file that builder creates. You can have as many module-header blocks, the code will just be all placed together in the header file.

Here is an example of a module-header:

    module-header{
    	#include <stdio.h>
    	#include "utility.h"
    }
    				

The builder doesn't do any validity checking in between the curly braces. So if you have typographical errors, you probably won't know until you try to compile the module.

module-init

This section allows you to specify native code that is executed when the module is loaded into a script. It is an optional section, but builder will create an empty module-init function in the C source.

The module's anonymous function (sometimes referred to as the _start function) is also executed when it is first imported, but module-init code is executed first. Also, the _start function cannot contain native code. So if your module initialization requires multiple jumps between native and ferite code, you can use the _start function to call native functions where necessary and use ferite code for everything else.

The syntax for creating a module-init section is similar to module-header:

    module-init{
    	...your code goes here...
    }
    				

This will cause all of the code placed within the curly braces to be placed in the module's init function. Incase you're interested, the build destination is the modulename_core.c file, in a function called modulename_init(). The function returns void and has 1 parameter, "FeriteScript *script", which is accessible to the code within the section.

module-deinit

This section is syntactically almost identical to the module-init section. Like module-init, module-deinit is not a required section. Again, the builder will create empty module-deinit function in the C source for you if you do not specify one.

Code in this section is executed when the script that loaded the module is being deleted. More precisely, it is run by a call to ferite_script_delete(). However, you usually don't have to worry about the specifics unless you're embedding ferite in your application. For most purposes, just know that this code is run when the script has finished executing.

Here is an example of a module-deinit section:

    module-deinit{
    	...your code goes here...
    }
    				

As you can see, it is basically the same as module-init. The return type is void, so you shouldn't try returning anything from this function. It also has the affected script passed into it, which is accessed exactly the same as you would for module-init.

module-register and module-unregister

When a native module's shared object is loaded, it's register function is called once. This allows the shared object to setup any system specific things. Symetrically, module-unregister is only called once, and that is when the ferite module system decides to unload the shared object. They are both blocks of code like module-init and module-deinit and should be used the same way.

Native Functions, the builder way

When developing a native module with builder it will be necessary to create functions that can be called by ferite scripts. To make this easy there are only two main differences between a ferite function and a native function. These are the keyword native and that the bodies of the functions are written in C.

First we'll start with an example of how to declare a simple native function:

    native function foo(){
    	...your code goes here...
    }
    				

This would result in the C source between the curly braces being placed in one of the C source files. The exact file and the exact function name created depends on the namespace or class that the function is declared in. This might vary from version to version so I won't get into it here, but feel free to look at the source created. You'll probably be able to figure it out from there. To a scripter the function looks and tastes the same as a normal ferite function.

It should be noted that within each function the following variables are accessable:

  • script - a pointer to the FeriteScript in which the function was called.

  • function - a pointer to the FeriteFunction which owns the function executing.

  • params - the null terminated list of parameters [see Calling Functions for more information].

  • self - Note! only for object methods, a pointer to a FeriteObject on which the function is being executed.

Parameters

The next step is to pass in some variables, and it is pretty easy to do. Simply declare the variables as you would normally do for any ferite script. When you get inside of the function, the values passed in will be converted to units that are workable in C with the same name. Complex objects will be presented to you in the form of pointers to different types of structs according to their type. All variables are available by the names you gave in the function declaration. Following is a quick breakdown of the different types and how they convert.

Table 3-1. Parameter Types

numberdouble
stringFeriteString *
objectFeriteObject *
arrayFeriteUnifiedArray *

  • number - Numbers are converted to doubles because doubles can represent LONG_MAX, and ferite numbers support floating point values anyways. If you expected to use the value as an integer in your function you can simply cast the double to a long. It is a good idea to check that the number passed in is not greater then LONG_MAX before you cast it to an long, otherwise you might end up with some funny looking results.

  • string - Strings are converted to FeriteString *, and their C-string values are accessible by struct element 'data'. So you can retrieve the value of string mystring by mystring->data. Following is an example that accesses a string's value by using it in a call to strdup().

        native function foo( string mystring ){
        	char *mystring_copy;
        	mystring_copy = strdup( mystring->data );
        }
        							
  • object - Objects are instances of classes, which must be accessed by reaching into ferite's internals. This is covered in the next chapter "Accessing Ferite Internals".

  • array - Arrays must also be accessed by reaching into ferite's internals. Again, this is covered in the next chapter "Accessing Ferite Internals".

Return Values

So now you can pass variables into functions. Next you need to know how to return values from functions. Any time you don't specify a return value and your function runs off the end of its scope, ferite will assume you meant to return void. If returning void is not the desired effect, or you would like to specify a position to return from other than running off the end of the function's scope, you will need to specifically return a value using one of the following C macros.

Note! By default a function generated by builder will automatically return void. You only need to specify returns if you want.

  • FE_RETURN_VOID - returns void to the caller, this is synonymous with not returning anything.

  • FE_RETURN_TRUE - returns true to the caller.

  • FE_RETURN_FALSE - returns false to the caller.

  • FE_RETURN_LONG( value ) - returns a number to the caller with the contents of the given long.

  • FE_RETURN_DOUBLE( value ) - returns a number to the caller with the contents of the given double.

  • FE_RETURN_STR( string, freeme ) - returns a FeriteString* to the caller. The parameter "string" is passed in as a FeriteString*. If freeme == FE_TRUE, string is freed using ferite's memory manager. if freeme == FE_FALSE, it is not freed at all.

  • FE_RETURN_ARRAY( pointer to array ) - returns an array to the caller.

  • FE_RETURN_OBJ( pointer to object ) - returns an object to the caller (objects are instances of classes).

  • FE_RETURN_NULL_OBJECT - returns a null object to the caller (useful for functions that are expected to return an object, but need to signify an error condition).

  • FE_RETURN_VAR( variable ) - returns a FeriteVariable to the caller. This allows you to return a variable that you have created yourself to the engine. It will tag the variable allowing ferite to clear it up when it is finished with. If you want to return a variable, but keep hold of it, you must simply return the variable as you would an item from a normal c function. eg:

        return someVar;
        								

All of these macros actually convert the given return values into a FeriteVariable * which is then returned to the caller. As a general rule, you should always use the available macros when mixing C and ferite to prevent your functions from breaking if the interface ever changes. These macros will be kept up to date, so you are safe to use them.

And Finally

The previous few sections should give you enough information to get you up on your feet and writing native functions. To play with ferite's internals you will need to read on where this is dicussed in depth.

Classes and Namespaces

Classes and namespaces in native modules work exactly like their non-native counterparts. You simply declare the namespace or class in the ferite-c file, and when a script tells ferite to import the module, ferite will parse the .fec and create the namespaces and classes as usual and link up the native functions from the shared object. There is absolutely no syntax change for creating classes and namespaces. Pretty easy isnt it?

There is, however, the added ability to place native functions within classes and namespaces. The syntax for doing so is no different that what you've already seen, just place them within the curly braces of the namespace or class that you would like them to be a part of.

Here is an example of a native function in a namespace:

    namespace foo {
    	native function bar() {
    		...your code goes here...
    	}
    }
    				

And here is an example of a native function in a class:

    class foo {
    	native function bar() {
    		...your code goes here...
    	}
    }
    				

You can also make functions in classes static, as was described in the user manual. So of course we can make those native functions as well. Simply place the keyword static in the function declaration.

    class foo{
    	static native function bar1(){
    		...your code goes here...
    	}
    	native static function bar2(){
    		...your code goes here...
    	}
    }
    				

Both bar1() and bar2() are native functions that are static within the class foo. The order of the keywords does not matter.

Object Data

When a native function belonging to an object is called, there is the self variable availible. This is a pointer to the FeriteObject which is currently executing. Now, to make life easier there is a part of the FeriteObject structure that allows you, the module writer, to attach any data to it. This is called [and refered to] as odata and is short for object data. Ferite does not and will never touch this, it is the job of the programmer to deal with it. It is very simple to use, simply access the odata member on self.

    self->odata = get_some_resource();
    					

Most of the time, the odata pointer is setup when the object is constructed and cleared up when the object is destroyed. This is simple to do as you merely write a native constructor and destructor. Most of the module code for ferite makes use of this feature and to make things more straight forward, a macro called SelfObj is declared casting odata into what ever form is stored there.

Example: In the 'Sys' module, odata is used to store a pointer to the FILE* pointer for file streams.

    #define SelfObj (FILE*)(self->odata)
    					

Finally

This chapter should have helped you get off your feet and understand the way in which builder can help you not only rapidly develop modules but keep them very close to ferite code. You should look at the .fec files that ship with ferite to clarify any doubts you have.



[previous] Builder[up][toc]Without Builder [next]
ferite et al © 2000-2004, Chris Ross