## 6 Using the C Server Back-end

### 6.1 Introduction

The mapping of OMG IDL to the C programming language when C Server switch is the back-end 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.

### 6.2 What Is the C-server Good For?

The C-server uses the same communication protocol as for the Erlang genservers, it is actually a 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.

### 6.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 encoding/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 his own switches if there is a need for this.

### 6.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 callback function that implements the request with the parameters followed 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

### 6.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 the client is an `int` which is positive or zero when the call is succesful, negative otherwise

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 the client is an `int` which is positive or zero when the call is succesful, negative otherwise

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 the client is an `int` which is positive or zero when the call is succesful, negative otherwise

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 positive 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 the client is a `pointer to the restore function` which is NULL when the restore function is not defined, initiated to point the restore function otherwise

Callback functions are implementation dependent and in order to make things work, the following rule must 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 appearence 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 the client is an `int` which is positive or zero when the call is successful, 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 appearence 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

### 6.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 types than the elementary IDL types are used by the IDL file definitions. All these files must be compiled and linked to the other code.

### 6.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`

### 6.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

### 6.9 Compiling the Code

In the Erlang shell type :

`ic:gen(<filename>, [{be, c_server}])`.

### 6.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 a C function that uses as 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.

### 6.11 An Example

In this example, a file "random.idl" generates code for the plain erlang back-end :

- 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 (BEAM) emulator version 4.9

          Eshell V4.9  (abort with ^G)
          1> ic:gen(random,[{be, c_server}]).
          Erlang IDL compiler version 3.2
          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 this 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) {

          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;
          } else
            oe_env->_received -= 2;

          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;

        oe_ei_encode_version(oe_env);
        oe_ei_encode_tuple_header(oe_env, 2);
        oe_ei_encode_ref(oe_env, &oe_env->_unique);

        /* Encode parameter: CORBA_double oe_result */
        if ((oe_error_code = oe_ei_encode_double(oe_env, 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 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 at the ic-3.2/examples/c-server directory.
  In the same directory all the code for this example can also be found.

| Due to changes inErl_Interface, to allow buffer expansion, a new receiving function `ei_receive_encoded/5` is created, while changes have been implemnted in CORBA_Environment initialization. You must consider and adapt these. The example in the `ic-3.2/examples/c-server` directory demonstrates the changes.

Running the example :

- Please read the ReadMe file att the ic-3.2/examples/c-server directory
  In the same directory all the code for this example can also be found.

Copyright © 1991-2002 Ericsson Utvecklings AB