Using the C Binding
This guide uses "xxx" as the service name. Substitute with the service name from your etch file.
Generated code
The C Binding compiler is called using
etch -b c -d . -w INTF,MAIN,IMPL,CLIENT,SERVER xxxNSDL.etch
It generates code for both sides (client and server). The main files you need to change manually to add your implementation are:
File |
Function |
xxx_client_main.c |
main method of client, starts runtime, connects to server, calls of server methods, disconnect, destroy runtime |
xxx_listener_main.c |
main method of server, starts runtime, waits for calls |
xxx_server_impl.c/.h |
implementations of server directed methods |
xxx_client_impl.c/.h |
implementations of client directed methods |
How to implement a method on the server / on the client
The following steps describe implementation of server-directed methods. Since Etch is symmetric, the client directed functions are implemented exactly the
same way in the client-related generated files.
Assume you have an IDL containing
@Direction(server)
int simpleFunction(int x)
- open xxx_server_impl.c
- declare an implementation of simpleFunction,e.g.:
etch_int32* simpleFunctionImpl(void* thisx, etch_int32* x);
| You can get the desired function signatures from xxx_interface.h. This file contains typedefs for all functions from the NSDL. |
- implement simpleFunction, e.g:
etch_int32* simpleFunctionImpl(void* thisx, etch_int32* x){
etch_int32* result;
result = new_int32(x->value + 1);
etch_object_destroy(x);
return result;
}
| The first parameter is an instance of the server_impl object. It also contains a reference to the calling client. You can use this for callbacks (if the call is @AsyncReceiver or @Oneway, otherwise you will end up in a deadlock). To do a callback, do the following:
((tester_server_impl*)thisx)->client->callbackFunctionName(((tester_server_impl*)thisx)->client, ...);
|
| Parameters of functions have to be destroyed by the function implementation using etch_object_destroy. |
- find function new_xxx_server_impl, this function is called when a client connects (the client is given as parameter)
- add the function pointer of simpleFunctionImpl to the pserver object in this function. This "registers" the function as the implementation of the call.
xxx_server_impl* new_xxx_server_impl(xxx_remote_client* client) {
.... pserver->simpleFunction = pserver_base->simpleFunction = simpleFunctionImpl;
}
- Thats it!
| If you want to return a user defined exception (which was defined in the NSDL), then simply return it in your function implementation instead of the usual return value. |
How to call a method on the server / on the client
As above, the instructions below are symmetrical for client and server. Server directed calls are called like this:
- open xxx_client_main.c
- find main method
- add your call after the starting of the runtime, e.g.
... etch_int32* result;
...
etch_status = tester_helper_remote_server_start_wait(remote, waitupms);
...
result = remote->simpleFunction(remote,new_int32(42));
if(is_etch_exception(result)){
wchar_t* extext = NULL;
ex = (etch_exception*)result;
if(etch_exception_get_message(ex)){
extext = etch_exception_get_message(ex)->v.valw;
}
.. }else{
.. }
.. etch_object_destroy(result);
}
| Result Objects have to be freed by the caller using etch_object_destroy. Parameters of calls will be freed by the runtime automatically. |
| You can get an built-in exception from any remote Etch call (e.g. in case of timeouts). This is why the code above contains is_etch_exception(result).
Please note that there are special is_blabla_exception test methods generated for used defined exception, which you can use to further refine the type of exception "thrown". |
Memory Management
The C Binding for Etch has the following memory management rules:
- Implementation side: Parameters of functions have to be destroyed by the function implementation using etch_object_destroy.
- Caller side: Result Objects have to be freed by the caller using etch_object_destroy. Parameters of calls will be freed by the runtime automatically.
All Etch Objects (including the primitive type wrappers) are freed using
etch_object_destroy(void* val)
Etch primitive data types
All primitive types have constructurs of the form
User defined struct types
Etch generates structs and constructors for user defined types.
Assume you have the following in your IDL:
struct simpleStruct (
int x,
int y
)
The compiler will generate (in xxx_interface.c/.h)
typedef struct tester_simpleStruct
{
etch_object object;
int x;
int y;
} tester_simpleStruct;
tester_simpleStruct* new_tester_simpleStruct();
You can pass those structs to Etch function calls just like primitive type wrappers. You can also use etch_object_destroy on them to free the memory.
Array types
Arrays in the IDL are implemented using the struct
You have to supply the correct CLASSID for the content for the C Binding to work properly. Here are samples for primitive and complex arrays:
Primitive Arrays
int values[4] = {0,1,2,3};
const int numdims = 1, itemsize = sizeof(int), itemcount = sizeof(values)/itemsize;
param = new_etch_nativearray_from (values, CLASSID_ARRAY_INT32,
itemsize, numdims, itemcount, 0, 0);
"numdims" is the number of dimensions of the array (e.g. int[] vs. int[][]). The last three parameters of new_etch_nativearray are the sizes of the (up to) three dimensions of the array. More dimensions are not supported.
Complex typed Arrays
Assume the NSDL with "simpleStruct" from above. Create an array of simplestruct using:
int arraysize = 5;
tester_simpleStruct** values = etch_malloc(arraysize * sizeof(tester_simpleStruct*),0);
... etch_nativearray* natarray = new_etch_nativearray_from(
values, CLASSID_ARRAY_STRUCT, sizeof(tester_simpleStruct*), 1, arraysize, 0, 0);
natarray->content_class_id = CLASSID_XXX_SIMPLESTRUCT;
natarray->content_obj_type = ETCHTYPEB_USER;
natarray->is_content_owned = TRUE;
...
| For complex typed arrays you have to supply the CLASSID of the content of the object. Those CLASSIDs are generated by the compiler for your user defined types. |
| The field is_content_owned tells Etch whether it should destroy the array content on array destruction, too. This will take effect when calling etch_object_destroy on the natarray or if it is destroyed by the runtime itself (e.g. when it is used as a remote call parameter, see above). |
|