![]() |
Using fflib.fs with gforth |
fflib.fs allows you to use C routines in your gforth code.
First you need to load fflib into your interpreter:
require fflib.fs
To use a C library, use the following word:
library name objectfile
fflib.fs uses compiled so objects. To use the object it must be in your library path.
For example, to use the standard C library this would be:
library libc libc.so.6
This makes a new word, libc, that we will use to define procedures that we can call in libc. They will look like this:
libc forth-name argument-types (return-type) c-name
Here are the currently supported argument types:
| Type | Description |
|---|---|
| int | integer |
| sf | single precission floating point |
| df | double precission floating point |
| llong | long long |
| ptr | pointer |
Here are the currently supported return types:
| Type | Description |
|---|---|
| void | void |
| int | integer |
| sf | single precission floating point |
| fp | double precission floating point |
| llong | long long |
| ptr | pointer |
For example, the libc command sleep is defined in c with the signature:
unsigned sleep(unsigned seconds);
libc sleep int (int) sleep
sleep. It will consume one integer from the stack, sleep for that many seconds, then put a zero on the stack.
Here's another example that uses strings. This is a little more difficult because c and forth use different representations for strings. C uses an address to an array of characters terminated with a null character. Forth uses an address to an array of characters, and a length of the list. These two routines convert between c and forth representations of strings. Note that going from a forth string to a c string requires a copy to be made to add the extra null byte.
: ctof ( addr -- addr u ) 0 ( addr 0 ) begin over over ( addr n addr n ) chars + ( addr n addr+n*chars ) c@ ( addr n char ) 0> ( addr n truth ) while ( addr n ) 1 + ( addr n+1 ) repeat ; : ftoc ( c-addr u -- 'c-addr ) here dup >r over allot swap cmove 0 c, align r> ;
These are the definitions of some routines for working with times in libc:
void gettime(struct time *);
time_t time(time_t *t);
struct tm *localtime(const time_t *tod);
size_t strftime(char *buf, size_t n, const char *format,
const struct tm *time_info);
We can import them into forth with the following commands. I chose to import time with the name libctime.
libc libctime ptr (int) time libc localtime ptr (ptr) localtime libc strftime ptr int ptr ptr (int) strftime
Here is an example of using these commands. First we create one cell in memory for the time_t structure, then a cell that holds 12 characters as a buffer.
create timet 1 cells allot create buf 12 chars allot buf 12 ( address of buf, 12 ) s" %A" ( add string address and length ) ftoc ( make a c style string from them ) timet libctime drop ( get the time into memory, drop the result ) timet localtime ( make the time information, returns a pointer ) strftime ( format the time into the buffer buf ) buf ctof ( convert c string to a forth string, adds character count ) type ( show the string )
Callbacks are defined in two parts. First we must describe the type of the callback. This is done via: For example, we could define a callback that takes three numbers and returns one. We'll call it 3to1
To demonstrate callbacks we'll need to use a library that utilizes them.
fflib.fs makes use of two previously undocumented words. These are These can be useful to pass a pointer to a c function as an argument to a c function. This could have significant performance advantages over a callback to a forth word that calls the c function. Here is our previous example, modified so that the button sends a destroy event, using gtk_widget_destroy:
callback callback-type-name (return-type) argument-types callback;
This will produce a new word, callback-type-name which will take an address of a word on the stack, and the name for the callback and will make a callback with that name:
address-of-word callback-type-name callback-name
callback 3to1 (int) int int int callback;
3to1 is now a word that takes an address to a word, and a name, and makes a callback with that name. First we'll need a word that matches this description of a callback. Here is a word that does that:
: timesplus * + ;
Then we hook our function into the callback like this:
' timesplus 3to1 *+
require fflib.fs
\ We need to make some C strings, so we include the forth to c routine from above:
: ftoc ( c-addr u -- 'c-addr ) here dup >r over allot swap cmove 0 c, align r> ;
library libgtk libgtk-x11-2.0.so.0
libgtk gtk_init ptr ptr (void) gtk_init
libgtk gtk_button_new_with_label ptr (ptr) gtk_button_new_with_label
libgtk gtk_container_add ptr ptr (void) gtk_container_add
libgtk gtk_main (void) gtk_main
libgtk gtk_main_quit (void) gtk_main_quit
libgtk gtk_widget_show ptr (void) gtk_widget_show
libgtk gtk_window_new int (ptr) gtk_window_new
libgtk g_print ptr (void) g_print
libgtk g_signal_connect_data ptr ptr ptr ptr ptr int (int) g_signal_connect_data
\ Define a few other things to match the way gtk has them defined
: g_signal_connect 0 0 g_signal_connect_data ;
: g_signal_connect_after 0 1 g_signal_connect_data ;
: g_signal_connect_swapped 0 2 g_signal_connect_data ;
\ gtk callback
callback libgtkcallback (void) ptr ptr callback;
\ gtk signal handler callback
callback libgtksignalhandler (int) ptr ptr ptr callback;
\Example callback made from nameless word
:noname drop drop s" Hello World " ftoc g_print ; libgtkcallback hello
\ Example making a callback with a name
\ We return 0 so that it closes
\ If we returned 1, it wouldn't
: delete_event drop drop drop s" delete event occured " ftoc g_print 0 ;
' delete_event libgtksignalhandler de
\ Another example callback
:noname drop drop gtk_main_quit ; libgtkcallback destroy
\ Make variables for a button and a window
variable button
variable window
\ Initialize gtk
0 0 gtk_init
\ Create a window
0 gtk_window_new window !
\ Connect the window to delete event
window @ s" delete_event" ftoc de 0 g_signal_connect drop
\ Connect the destroy handler
window @ s" destroy" ftoc destroy 0 g_signal_connect drop
\ Create a button labeled hello world
s" Hello World" ftoc gtk_button_new_with_label button !
\ Connect the button to the hello callback
button @ s" clicked" ftoc hello 0 g_signal_connect drop
\ Add the button to the window
window @ button @ gtk_container_add
\ Show the buton and the window
button @ gtk_widget_show
window @ gtk_widget_show
\ Run the main procedure
gtk_main
More Topics
open-lib and lib-sym.
open-lib takes an address of a forth string, and its length off of the stack, tries to open and load a library by that name, and places the address of the loaded library onto the stack. If it fails it places 0 on the stack instead. For example:
s" libc.so.6" open-lib
lib-sym takes the address of a forth string, its length, and the address of a loaded library off of the stack. It then tries to locate the symbol of that name in the library and places its address on the stack. If it fails it places 0 on the stack instead. For example:
s" sleep" s" libc.so.6" open-lib lib-sym
require fflib.fs
\ We need to make some C strings, so we include the forth to c routine from above:
: ftoc ( c-addr u -- 'c-addr ) here dup >r over allot swap cmove 0 c, align r> ;
library libgtk libgtk-x11-2.0.so.0
libgtk gtk_init ptr ptr (void) gtk_init
libgtk gtk_button_new_with_label ptr (ptr) gtk_button_new_with_label
libgtk gtk_container_add ptr ptr (void) gtk_container_add
libgtk gtk_main (void) gtk_main
libgtk gtk_main_quit (void) gtk_main_quit
libgtk gtk_widget_show ptr (void) gtk_widget_show
libgtk gtk_window_new int (ptr) gtk_window_new
libgtk g_print ptr (void) g_print
libgtk g_signal_connect_data ptr ptr ptr ptr ptr int (int) g_signal_connect_data
\ Define a few other things to match the way gtk has them defined
: g_signal_connect 0 0 g_signal_connect_data ;
: g_signal_connect_after 0 1 g_signal_connect_data ;
: g_signal_connect_swapped 0 2 g_signal_connect_data ;
\ gtk callback
callback libgtkcallback (void) ptr ptr callback;
\ gtk signal handler callback
callback libgtksignalhandler (int) ptr ptr ptr callback;
\Example callback made from nameless word
:noname drop drop s" Hello World " ftoc g_print ; libgtkcallback hello
\ Example making a callback with a name
\ We return 0 so that it closes
\ If we returned 1, it wouldn't
: delete_event drop drop drop s" delete event occured " ftoc g_print 0 ;
' delete_event libgtksignalhandler de
\ Another example callback
:noname drop drop gtk_main_quit ; libgtkcallback destroy
\ Make variables for a button and a window
variable button
variable window
\ Initialize gtk
0 0 gtk_init
\ Create a window
0 gtk_window_new window !
\ Connect the window to delete event
window @ s" delete_event" ftoc de 0 g_signal_connect drop
\ Connect the destroy handler
window @ s" destroy" ftoc destroy 0 g_signal_connect drop
\ Create a button labeled hello world
s" Hello World" ftoc gtk_button_new_with_label button !
\ Connect the button to the hello callback
button @ s" clicked" ftoc hello 0 g_signal_connect drop
\ Connect the button to an existing c routine
button @ s" clicked" ftoc
s" gtk_widget_destroy" s" libgtk-x11-2.0.so.0" open-lib lib-sym
window @ g_signal_connect_swapped
\ Add the button to the window
window @ button @ gtk_container_add
\ Show the buton and the window
button @ gtk_widget_show
window @ gtk_widget_show
\ Run the main procedure
gtk_main