KwMap.net - browse the Keyword Map of ferite.org
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.
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.
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.)
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.
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:
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.
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:
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.
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:
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.
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.
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:
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:
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
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.
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.
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:
And here is an example of a native function in a class:
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.
Both bar1() and bar2() are native functions that are static within the class foo. The order of the keywords does not matter.
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.
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.
|ferite et al © 2000-2004, Chris Ross|