How To Program Components In C/C++
Introduction
What is a component?
Gambas components are shared libraries written in C or C++ that add new functions to the Gambas interpreter.
They act like Linux drivers towards the Linux kernel:
-
The components and the interpreter communicate through the Gambas Programming Interface.
-
They must be compiled inside the Gambas source package.
-
They are executed in the interpreter environment, and so must not do whatever they want.
A component can contain:
-
New classes that are added to the other Gambas classes. These classes are declared to the interpreter using a C structure that contains the description of each method, constant and event.
-
Interpreter Hooks. These are special functions that implement important interpreter operations, like managing the event loop, reading the command-line arguments...
-
An optional programming interface that enhances the Gambas Programming Interface, and that other components can use.
A component must have a name. This name is the the word
gb
followed by a dot-separated list of words describing
the role of the component and another component it relies on, if any.
Example
-
gb.qt4
is a component based on Qt4 that brings GUI programming to Gambas.
-
gb.net.curl
is a component based on the libcurl library. It requires the gb.net
component, i.e. loading the first implies loading the second.
The Gambas Programming Interface
The Gambas Programming Interface is a set of utilities, functions and macros that allows you to:
-
Describe the component.
-
Define interpreter hooks.
-
Allocate and free memory.
-
Manipulate arrays, hash tables...
-
Manipulate Gambas native datatypes.
-
Create Gambas arrays and collections.
-
Raise events.
-
Manipulate Gambas objects.
-
Load a file from the project archive.
-
Load other components.
-
...
The use of this programming interface is highly recommended, as it prevents a component from doing weird things.
The Gambas Programming Interface is a C structure that contains one function pointer for each function of the
interface.
This structure is declared in the main file of your component and is automatically initialized by the interpreter at component loading.
Writing Good Components
Writing good components is the difficult part! Let's suppose you want to write a SDL component, i.e. a component
that lets a Gambas project use all the power of the SDL library: graphics, sound, CD-ROM...
You can be happy with just wrapping the library functions, structure and constants.
It is the easy way, even if wrapping C structures and functions may not be possible in every case with Gambas.
Your component will be useful, but not very interesting for the Gambas programmer, because he will have to program in C or C++ style.
Instead, you should rack your brain to create a different interface between Gambas and the library,
by trying to generalize and simplify the original interface of the library.
That is what I did with
gb.gui: it is far easier to use the
gb.qt4 component than programming the Qt library directly.
Moreover, the
gb.gtk component use the same interface than gb.qt4, and so you can write a GUI program that can use one or the other indifferently.
Component Source Organization
The source files of a component are stored in its own directory.
-
Either in a project root directory, having the same name as the component (gb.xxxx).
-
Either in a sub-directory of
/main/lib
.
Use the first case if your component needs one or more shared library.
Use the other one if your component just needs a very basic library already used by the interpreter, like
libc
or
libm
.
A typical component directory includes:
-
The autoconf/automake stuff, i.e. a Makefile.am file that describes how to compile the component.
-
One source file and one header file for each main class implemented in the component.
-
One main file that implements the entry points of the component.
-
One component description file.
-
A Gambas project if the component has a Gambas part. The name of the project must be the same as the component name.
Here is the contents of the
gb.dbus
directory that is not generated by the autoconf/automake tools:
gb.dbus
├── acinclude.m4 -> ../acinclude.m4 Common autoconf macros
├── AUTHORS File required by GNU tools
├── ChangeLog File required by GNU tools
├── component.am -> ../component.am Makefile.am part needed by components
├── configure.ac The autoconf configuration file
├── gambas.h -> ../main/share/gambas.h The Gambas interpreter API header
├── gb_common.h -> ../main/share/gb_common.h Some other useful common declarations
├── INSTALL -> ../INSTALL File required by GNU tools
├── m4 -> ../m4 Directory including common m4 macros used by configure.ac and acinclude.m4
├── Makefile.am The top-level automake configuration file
├── missing -> ../missing File required by GNU tools
├── NEWS File required by GNU tools
├── README README file
├── reconf -> ../reconf Link at a script that reconfigure the source package from scratch
└── src The source directory
├── c_dbus.c
├── c_dbusconnection.c
├── c_dbusconnection.h
├── c_dbus.h
├── c_dbusobserver.c
├── c_dbusobserver.h
├── c_dbusvariant.c
├── c_dbusvariant.h
├── dbus_print_message.c
├── dbus_print_message.h
├── gb.dbus The Gambas part of the gb.dbus component
│ ├── .project
│ └── .src
│ ├── CTest2.class
│ ├── CTest.class
│ ├── DBusApplication.class
│ ├── DBus.class
│ ├── DBusObject.class
│ ├── DBusProxy.class
│ ├── DBusSignal.class
│ └── MMain.module
├── gb.dbus.component The component description file
├── helper.c
├── helper.h
├── main.c
├── main.h
└── Makefile.am The automake configuration file of the "src" sub-directory
There is a
TEMPLATE
directory in the sources that includes a script named
make-component
that
can create a component skeleton directory from a configuration file.
The configuration file must be located in the
TEMPLATE/conf
sub-directory.
See the
TEMPLATE/README
file for more details.