Adding a frontend language to the breve engine: the breve object API
<< previous | Chapter 14. The breve Source Code | next >>

## Adding a frontend language to the breve engine: the breve object API

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”).

### Structures used by the breve Object API

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”.

### Callbacks required to add a new frontend language

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.

### Specifying a new breve object type, and

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 );

### Testing and using the object frontend

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.

<< previous | Up | next >>
Chapter 14. The breve Source Code | Table of Contents | Embedding breve In Another Program