Previous Up

3.4  Passing generic C or C++ data to ECLiPSe

It is possible to include any C or C++ data in an ECLiPSe term. To do this it is wrapped into a handle to tell ECLiPSe that this is external data. You also have to supply a method table, which is a set of functions that are called when ECLiPSe wants to make common operations that it assumes can be done on any data (eg. comparing, printing).

3.4.1  Wrapping and unwrapping external data in an ECLiPSe term

To create an ECLiPSe wrapper for a C/C++ object, the function handle() is used. It takes a pointer to any C or C++ data, and a pointer to a suitable method table (t_ext_type structure) and creates an ECLiPSe handle term which refers to them. Method tables for the common case of arrays of char, long or double are predefined. For example a handle for a double array is made like this

    double my_array[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
    EC_word w = handle(&ec_xt_double_arr, my_array);

where ec_xt_double_arr is a predefined method table for arrays of doubles. To convert back from an ECLiPSe term is_handle() is used. The method table passed in indicates the sort of data you expect to get. If the ECLiPSe handle contains the wrong sort, the function returns TYPE_ERROR:

    if ((EC_succeed == w.is_handle(&ec_xt_double_arr, &obj))
        obj->my_method();
    else
        cerr << "unexpected type\n";

The creation of the handle copies the address of the array, rather than the array itself, so the handle must be used within the scope of the array.

3.4.2  The method table

Apart from the predefined method tables ec_xt_double_arr, ec_xt_long_arr and ec_xt_char_arr, new ones can easily be defined. The address of the table is used as a type identifier, so when you get an external object back from ECLiPSe you can check its type to determine the kinds of operations you can do on it. You can choose not to implement one or more of these functions, by leaving a null pointer ((void*)0) in its field.

    typedef void *t_ext_ptr;

    typedef struct {
        void       (*free)       (t_ext_ptr obj);
        t_ext_ptr  (*copy)       (t_ext_ptr obj);
        void       (*mark_dids)  (t_ext_ptr obj);
        int        (*string_size)(t_ext_ptr obj, int quoted);
        int        (*to_string)  (t_ext_ptr obj, char *buf, int quoted);
        int        (*equal)      (t_ext_ptr obj1, t_ext_ptr obj2);
        t_ext_ptr  (*remote_copy)(t_ext_ptr obj);
        EC_word    (*get)        (t_ext_ptr obj, int idx);
        int        (*set)        (t_ext_ptr obj, int idx, EC_word data);
    } t_ext_type;
void free(t_ext_ptr obj)
This is called by ECLiPSe if it loses a reference to the external data. This could happen if the ECLiPSe execution were to fail to a point before the external object was created, or if a permanent copy was explicitly removed with built-ins like setval/2, erase/2 or bag_dissolve/2. Note that an invocation of this function only means that one reference has been deleted (while the copy function indicates that a reference is added).
t_ext_ptr copy(t_ext_ptr obj)
This is called by ECLiPSe when it wants to make a copy of an object. This happens when calling ECLiPSe built-ins like setval/2 or recordz/2 which make permanent copies of data. The return value is the copy. If no copy-method is specified, these operations will not be possible with terms that contain an object of this type. A possible implementation is to return a pointer to the original and e.g. increment a reference counter (and decrement the counter in the free-method correspondingly).
void mark_dids( t_ext_ptr obj)
This is called during dictionary garbage collection. If an external object contains references to the dictionary (dident) then it needs to mark these as referenced.
int string_size(t_ext_ptr obj, int quoted)
int to_string(t_ext_ptr obj,char *buf, int quoted)
When ECLiPSe wants to print an external object it calls string_size() to get an estimate of how large the string would be that represents it. This is used by ECLiPSe to allocate a buffer. The string representation must be guaranteed to fit in the buffer.

Finally the to_string() function is called. This should write the string representation of the object into the buffer, and return the actual size of that string.

int equal(t_ext_ptr obj1, t_ext_ptr obj2)
This is called when two external objects are unified or compared. Prolog views the external object as a ground, atomic element. It should return non-zero if the objects are considered equal.
t_ext_ptr remote_copy(t_ext_ptr obj)
This is called by parallel ECLiPSe when it needs to make a copy of an object in another worker. If the object is in shared memory, this method can be the same as the copy method.
EC_Word get(t_ext_ptr obj, int idx)
Returns the value of a field of the C/C++ object. This methods gets invoked when the ECLiPSe predicate xget/3 is called. The get/set method pair determines the mapping of index values to fields.
int set(t_ext_ptr obj, int idx, EC_word data)
Set the value of a field of the C/C++ object. This methods gets invoked when the ECLiPSe predicate xset/3 is called. The get/set method pair determines the mapping of index values to fields.

Example of the simplest possible user-defined method table:

    /* the initializer is actually not needed, NULLs are the default */
    t_ext_type my_type = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    my_struct data_in;
    ...
    // creating a handle for data_in
    EC_word w = handle(&my_type, &data_in);
    ...
    // checking a handle and extracting the data pointer
    my_struct *data_out;
    if ((EC_succeed == w.is_handle(&my_type, &data_out))
        data_out->my_method();
    else
        cerr << "unexpected type\n";

Previous Up