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).
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.
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;
dident
) then
it needs to mark these as referenced.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.
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";