This section describes how to incorporate another frontend language with breve. Using this information, you'll be able to use the breve engine from any language that can interface a C library.
The following steps are required to set up a language frontend in breve.
Write special callbacks for the language frontend (the section called “Callbacks required to add a new frontend language”).
Construct a new breve object type structure, brObjectType, and add classes to the breve engine (the section called “Specifying a new breve object type, and ”).
Test the language frontend using the existing breve program (the section called “Testing and using the object frontend”).
If desired, connect the language frontend to an application frontend or write your own customized frontend (the section called “Embedding breve In Another Program”).
The following structures are used by the breve Object API. These
structures are passed to the frontend language callback functions. See
the file kernel/breveObjectAPI.h
and its
documentation for a full description of these structs.
brObjectType
: information and
callbacks for a language frontend
brObject
: a class in any frontend
language
brInstance
: an instance in any
frontend language
brMethod
: information about a
method in an object
You should also familiarize yourself with the brEval
structure and types which are described in
the section called “Writing C Wrapper Functions Around Existing Code”.
In order to provide a language frontend to breve, a set of callbacks must be defined.
findObject
: locate a class by name.
void *(*findObject)( void *inObjectTypeUserData, char *name );
When breve encounters a class name that it does not recognize, it
will use this function to attempt to lookup the object. The
pointer that is returned will be placed in the pointer
field of a brObject
. This pointer will likely be used
later to instantiate objects or locate instance methods by your
instantiate
or findMethod
callbacks.
findMethod
: locate a method for a
class by name and argument count.
void *(*findMethod)( void *inObjectUserData, const char *inName, unsigned char *inTypes, int inTypeCount );
When breve needs to call a method in an object, it will be looked
up using this callback. The inObjectUserData
argument refers to the object
in which the method can be found—it is the pointer returned
by your findObject
callback. The
inName
argument is the name of the
method to look for.
The inTypeCount
argument gives the
number of arguments that will be passed to the method, and the
inTypes
argument contains the
argument types. The types are described in the section called “Writing C Wrapper Functions Around Existing Code”
instantiate
: create an instance of
an object.
brInstance *(*instantiate)( brEngine *inEngine, brObject *inObject, const brEval **inArguments, int inArgCount )
breve calls this function to create a new instance of a class.
The inObject
argument specifes the
object to be created and contains the user data found with
findObject
in the userData
field.
The function is expected to add the newly created instance to the
breve engine by calling brEngineAddInstance
. brEngineAddInstance
returns a brInstance
, which should in turn be returned by
instantiate
. brEngineAddInstance
is called as follows:
brInstance *brEngineAddInstance( brEngine *inEngine, brObject *inObject, void *inInstanceUserData );
The constructor arguments inArguments
and count inArgCount
are currently unused.
callMethod
: trigger a method call
in the frontend language.
int (*callMethod)(brInstance *instancePointer, brMethod *method, brEval **arguments, brEval *result);
The breve engine will need to trigger method calls in the frontend language for a number of events such as iteration and collision handling. This callback is used to trigger such events.
The callback is given the instance to be used, a brInstance
instance structure (which contains a
native language instance pointer in the "pointer" field); the
method to be called, a brMethod
structure (which contains a native language method pointer in the
"pointer" field); an array of brEval
argument pointers, and a pointer to an
output brEval
structure.
The callback should trigger the method call method
for the instance
instancePointer
. It
expects that the arguments
array contains the
number of items specified by the brMethod
structure's argumentCount field.
isSubclass
: determine whether a
class is a subclass of another.
int (*isSubclass)(brObject *class1, brObject *class2);
In order to correctly handle certain interactions like collisions, the breve engine needs to know whether one class is a subclass of another.
This callback is given two breve object pointers, and must return
1 if class1
is a
subclass of class2
, and
0 otherwise.
destroyObject:
release memory
allocated by your findObject
callback.
void (*destoryObject)(void *objectData);
If your findObject
callback
allocates memory, this callback should release that memory.
destroyInstance:
release memory
allocated by your instantiate
callback.
void (*destoryInstance)(void *instanceData);
If your instantiate
callback
allocates memory, this callback should release that memory.
destroyMethod:
release memory
allocated by your findMethod
callback.
void (*destoryMethod)(void *methodData);
If your findMetho
callback
allocates memory, this callback should release that memory.
destroyObjectType:
release the
memory associated with your brObjectType
.
void (*destroyData)(void *objectTypeData);
If you have allocate memory to be placed in the data
field of the brObjectType
, this callback should release that
memory.
Every object in a frontend language that will have instances in the
breve engine must be registered with the breve engine. Moreover, when
an object is added to the breve engine, it must also tell the engine
what "type" of object it is. Each object "type" corresponds to a
different language frontend and a different set of callbacks, so for
each language frontend one creates, one must also create a brObjectType
structure which contains the proper
callbacks. The structure is shown below.
struct brObjectType { /** * Finds a method in a given class */ void *(*findMethod)( void *inObject, const char *inName, unsigned char *inTypes, int inTypeCount ); /** * Finds an object class in the given language frontend */ void *(*findObject)( void *inObjectType, const char *inName ); /** * Creates a new instance of the given class. The constructor arguments are currently unused. */ brInstance *(*instantiate)( brEngine *inEngine, brObject *inObject, const brEval **inArguments, int inArgCount ); /** * Calls a method in the language frontend */ int (*callMethod)( void *inInstance, void *, const brEval **, brEval * ); /** * Returns 1 if parent is a subclass of parent. */ int (*isSubclass)( void *inChild, void *inParent ); /** * Destroys an instance of a language object previously created with instantiate. */ void (*destroyObject)( void *inObject ); /** * Destroys an instance of a language method previously created with findMethod. */ void (*destroyMethod)( void *inMethod ); /** * Destroys an instance of a language instance previously created with instantiate. */ void (*destroyInstance)( void *inInstance ); /** * Frees any leftover memory associated with the frontend, typically _userData. */ void (*destroyObjectType)( void *inObjectType ); /** * A function to execute code in this frontend language. */ int (*evaluate)( brEngine *inEngine, char *inFilename, char *inFiletext ); /** * A user-data callback pointer. */ void *_userData; /** * A unique identifier which will be set for all objects of this object type. This * identifier can be used to determine whether an instance or object is native to * a certain object type. */ long _typeSignature; };
Only one brObjectType
is required for
each language frontend.
Once the language frontend is defined, you can add it to the engine with the following function:
void brEngineRegisterObjectType( brEngine *engine, brObjectType *type );
Once you have written code to setup and register your new breve object type, you'll want to test that breve can locate objects, instantiate them and call their methods. To do so, you can add your frontend object type to the breve engine, and then instantiate an object from steve code.
To add your frontend object type to the breve engine, edit the file
kernel/frontendAPI.c
, and look at the
function breveFrontendInit
. The existing
breve applications call this function to create a breve engine. At the
bottom of this function, you can create and register your custom
language frontend.
brEngineRegisterObjectType(frontend->engine, functionToCreateLanguageFrontend
());
After recompiling breve with this change, it should be possible to
instantiate an object from your new language frontend from directly
within a steve simulation. See the Java example java/JavaTest.tz
for more information.