7 Using the C Server back-end
7.1 Introduction
The mapping of OMG IDL to the C programming language when C Server switch is the backend of choice is identical to the one use in C IDL mappning. The only difference is on the generated code and that the idl functions are translated to C function calls for the C Server.
7.2 What is the C-server good for ?
The C-server is using the same communication protocol as for the Erlang genservers, it is actually an C-genserver.So the C-server can be used for :
- Serving C-clients generated by the C-client back-end.
- Serving Erlang genserver-clients generated by the Erlang genserver back-end.
7.3 What kind of code is produced ?
The code produced is a collection of :
- C source files that contain interface code.
These files are named after the< Scoped Interface Name >__s.c
convension
- C source files that contain code for :
- type conversion
- memory allocation
- data enoding / decoding into buffers
- C header files that contain function headers and type definitions.
All functions found in the code are exported. The user is free to define it's own switches if there is a need for this.
7.4 What does this code do when used ?
The main functionality of a C server switch is to :
- Decode call requests stored in buffers
- Recognize the function noted in a request
- Call the calback function that implements the request with the parameters folowed in the message
- Collect the output from the callback function (if the function defined is not a
cast
)
- Encode the output value to an output buffer
- Call the restore function ( if defined ) that frees memory or/and sets up a server state
7.5 What is the interface of the functions produced ?
The C source defines the following functions :
- One server switch for each interface.
- One generic message decoder for each switch.
- One specific call function for each function defined in the interface.
- At most one specific parameter decoding function for each call function.
- One callback function for each call function.
- At most one specific return value encoding function for each call function.
The interface for the server switch is :
int < Scoped Interface Name >__switch(< Interface Object > oe_obj, CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
- the return value for is an
int
which is positiv or zero when the call is succed, negative otherwize
The interface for the generic message decoder is :
int < Scoped Interface Name >__call_info((< Interface Object > oe_obj, CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
- the return value for is an
int
which is positiv or zero when the call is succed, negative otherwize
The interface for the specific call function definition is :
int < Scoped Function Name >__exec(< Interface Object > oe_obj, CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
- the return value for is an
int
which is positiv or zero when the call is succed, negative otherwize
The interface for the specific parameter decoder function is :
int < Scoped Function Name >__dec( < Interface Object > oe_obj, < Parameters > CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
< Parameters >
are pointers to parameters for the function call to be decoded. The order of apearence is similar to the IDL definition of the function.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
- the return value for is an
int
which is positiv or zero when the call is succeed, negative otherwize
The interface for the specific callback function is :
< Scoped Function Name >__rs* < Scoped Function Name >__cb( < Interface Object > oe_obj, < Parameters > CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
< Parameters >
are pointers to in/out-parameters for the function call. The order of apearence is similar to the IDL definition of the function.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
- the return value for is a
pointer to the restore function
which can be NULL when the restor function is not defined, initiated to point the restore function otherwize
Callback functions are implementation dependent and in order to make things work, the following rule have to be followed when passing arguments to callback functions :
in
parameters of variable storage type are passed as is.out
parameters of variable storage type are passed by a pointer to their value.in / out
parameters of fixed storage type are passed by a pointer to their value.return
values are always passed by a pointer to their value.The interface for the specific message encoder function is :
int < Scoped Function Name >__enc( < Interface Object > oe_obj, < Parameters > CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
< Parameters >
are pointers to parameters for the return message to be encoded. The order of apearence is similar to the IDL definition of the function.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
- the return value for is an
int
which is positiv or zero when the call is succeed, negative otherwize
The encoder function is generated only for usual call IDL-functions (not oneways)
The interface for the specific restore function is :
void < Scoped Function Name >__rs( < Interface Object > oe_obj, < Parameters > CORBA_Environment *oe_env );
Where :
< Interface Object > oe_obj
is the client interface object.
< pointers to result values / parameters >
are pointers to in/out-parameters for the function call. The order of apearence is similar to the IDL definition of the function.
CORBA_Environment *oe_env
is a pointer to the current client environment as described in section 3.6.
The restore function type definition is recorded on the interface header file. It is unique for each IDL defined interface function
7.6 Functions used for internal purposes
Depending on the data defined and used in the IDL code, C-source files may be generated that hold functions used internally. This is the case when other than the elementary IDL types are used by the IDL file definitions. All these files must be compiled and linked to the other code.
7.7 Which header files to include ?
The only header files that must be included are the interface files, the files named
< Scoped Interface Name >__s.h
7.8 Which directories/libraries/options must be included under C-compiling?
Under compilation you will have to include :
- the directory
$OTPROOT/ usr/ include
Under linking you will have to link with :
- the libraries under
$OTPROOT/ usr/ lib
- -lerl_interface -lei -lnsl -lsocket -lic
7.9 Compiling the code
In the Erlang shell type :
ic:gen(
<filename>
, [{be, c_server}]).7.10 Implementing the callback functions
For each IDL interface
<interface name>
defined in the IDL file :
- Create the coresponding C file that will hold the C callback functions for the IDL defined functions.
- The implementation file does not need a special naming.
For each function defined in the IDL interface :
- Implement an C function that takes as the arguments in the same order as the input arguments described in the IDL file and returns the value described in the interface.
- When using the function, follow the mapping described in chapter 3.
7.11 An example
In this example, a file "random.idl" is generates code for the plain erlang backend :
- Main file : "random.idl"
module rmod { interface random { double produce(); oneway void init(in long seed1, in long seed2, in long seed3); }; };Compile the file :
Erlang (JAM) emulator version 4.7.1 Eshell V4.7.1 (abort with ^G) 1> ic:gen(random,[{be, c_genserver}]). Erlang IDL compiler version 3.0 ok 2>When the file "random.idl" is compiled it produces five files, two for the top scope, two for the interface scope, and one for the module scope. The header files for top scope and interface are empty and not shown here. In theis case only the file for the interface
rmod_random.erl
is important :.
- C file for interface : "rmod_random__s.c"
#include <string.h> #include "ic.h" #include "erl_interface.h" #include "ei.h" #include "rmod_random__s.h" /* * Main switch */ int rmod_random__switch(rmod_random oe_obj, CORBA_Environment *oe_env) { int status=0; /* Initiating exception indicator */ oe_env->_major = CORBA_NO_EXCEPTION; /* Call switch */ if ((status = rmod_random__call_info(oe_obj, oe_env)) >= 0) { if (strcmp(oe_env->_operation, "produce") == 0) return rmod_random_produce__exec(oe_obj, oe_env); if (strcmp(oe_env->_operation, "init") == 0) return rmod_random_init__exec(oe_obj, oe_env); /* Bad call */ CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_OPERATION, "Invalid operation"); return -1; } /* Exit */ return status; } /* * Returns call identity */ int rmod_random__call_info(rmod_random oe_obj, CORBA_Environment *oe_env) { char gencall_atom[10]; int error_code = 0; int rec_version = 0; oe_env->_iin = 0; oe_env->_received = 0; ei_decode_version(oe_env->_inbuf, &oe_env->_iin, &rec_version); ei_decode_tuple_header(oe_env->_inbuf, &oe_env->_iin, &oe_env->_received); ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, gencall_atom); if (strcmp(gencall_atom, "$gen_cast") == 0) { ei_decode_tuple_header(oe_env->_inbuf, &oe_env->_iin, &oe_env->_received); ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_env->_operation); oe_env->_received -= 1; return 0; } if (strcmp(gencall_atom, "$gen_call") == 0) { ei_decode_tuple_header(oe_env->_inbuf, &oe_env->_iin, &oe_env->_received); if ((error_code = ei_decode_pid(oe_env->_inbuf, &oe_env->_iin, &oe_env->_caller)) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Bad Message, bad caller identity"); return error_code; } if ((error_code = ei_decode_ref(oe_env->_inbuf, &oe_env->_iin, &oe_env->_unique)) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Bad Message, bad message reference"); return error_code; } if ((error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_env->_operation)) < 0) { ei_decode_tuple_header(oe_env->_inbuf, &oe_env->_iin, &oe_env->_received); if ((error_code = ei_decode_atom(oe_env->_inbuf, &oe_env->_iin, oe_env->_operation)) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_OPERATION, "Bad Message, cannot extract operation"); return error_code; } oe_env->_received -= 1; return 0; } else { oe_env->_received -= 2; return 0; } } CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Bad message, neither cast nor call"); return -1; } int rmod_random_produce__exec(rmod_random oe_obj, CORBA_Environment *oe_env) { if (oe_env->_received != 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_PARAM, "Wrong number of operation parameters"); return -1; } else { rmod_random_produce__rs* oe_restore = NULL; CORBA_double oe_result = 0; /* Callback function call */ oe_restore = rmod_random_produce__cb(oe_obj, &oe_result, oe_env); /* Encoding reply message */ rmod_random_produce__enc(oe_obj, oe_result, oe_env); /* Restore function call */ if (oe_restore != NULL) (*oe_restore)(oe_obj, &oe_result, oe_env); } return 0; } int rmod_random_produce__enc(rmod_random oe_obj, CORBA_double oe_result, CORBA_Environment *oe_env) { int oe_error_code; oe_env->_iout = 0; ei_encode_version(oe_env->_outbuf, &oe_env->_iout); ei_encode_tuple_header(oe_env->_outbuf, &oe_env->_iout, 2); ei_encode_ref(oe_env->_outbuf, &oe_env->_iout, &oe_env->_unique); /* Encode parameter: CORBA_double oe_result */ if ((oe_error_code = ei_encode_double(oe_env->_outbuf, &oe_env->_iout, oe_result)) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_PARAM, "Bad operation parameter on encode"); return oe_error_code; } return 0; } int rmod_random_init__exec(rmod_random oe_obj, CORBA_Environment *oe_env) { if (oe_env->_received != 3) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_PARAM, "Wrong number of operation parameters"); return -1; } else { int oe_error_code = 0; rmod_random_init__rs* oe_restore = NULL; CORBA_long seed1; CORBA_long seed2; CORBA_long seed3; /* Decode parameters */ if((oe_error_code = rmod_random_init__dec(oe_obj, &seed1, &seed2, &seed3, oe_env)) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, BAD_PARAM, "Bad parameter on decode"); return oe_error_code; } /* Callback function call */ oe_restore = rmod_random_init__cb(oe_obj, &seed1, &seed2, &seed3, oe_env); /* Restore function call */ if (oe_restore != NULL) (*oe_restore)(oe_obj, &seed1, &seed2, &seed3, oe_env); } return 0; } int rmod_random_init__dec(rmod_random oe_obj, CORBA_long* seed1, CORBA_long* seed2, CORBA_long* seed3, CORBA_Environment *oe_env) { int oe_error_code; if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, seed1)) < 0) return oe_error_code; if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, seed2)) < 0) return oe_error_code; if ((oe_error_code = ei_decode_long(oe_env->_inbuf, &oe_env->_iin, seed3)) < 0) return oe_error_code; return 0; }The implementation file must be a c file, in this example we use a file called
callbacks.c
. This file must be implemented implemented in a similar way :#include <stdlib.h> #include "rmod_random__s.h" rmod_random_produce__rs* rmod_random_produce__cb(rmod_random oe_obj, double *rs, CORBA_Environment *oe_env) { *rs = (double) rand(); return (rmod_random_produce__rs*) NULL; } rmod_random_init__rs* rmod_random_init__cb(rmod_random oe_obj, long* seed1, long* seed2, long* seed3, CORBA_Environment *oe_env) { srand(*seed1 * *seed2 * *seed3); return (rmod_random_init__rs*) NULL; }Compiling the code :
- Please read the
ReadMe
file att theic-3.0/examples/c-server
directory
In the same directory you can find all the code for this example
Running the example :
- Please look the
ReadMe
file att theic-3.0/examples/c-server
directory
In the same directory you can find all the code for this example