C mappings: From IDL types to C types

## Contents

- Your rights

- Introduction

- Component model

- Component description file

- GenoM IDL mappings

  - `C` mappings

    - Scoped name

    - Constant

    - Basic type

    - Enumerated type

    - String

    - Array

    - Struct

    - Union

    - Sequence

    - Optional

    - Port

    - Remote

    - Native

    - Exception

  - `C++` mappings

    - Scoped name

    - Constant

    - Basic type

    - Enumerated type

    - String

    - Array

    - Struct

    - Union

    - Sequence

    - Optional

    - Port

    - Remote

    - Native

    - Exception

- Running GenoM

- Tutorials

- Templates

## `C` mappings: From IDL types to `C` types

### Scoped names

The `C` mappings always use the global name for a type or a constant. The `C` global name corresponding to a `genom3` IDL global name is derived by converting occurrences of "::" to "_" (an underscore) and eliminating the leading underscore.

### Mapping for constants

In `C`, constants defined in dotgen are mapped to a `C` constant. For instance, the following IDL:

          const long longint = 1;
          const string str = "string example";

would map into

          const uint32_t longint = 1;
          const char *str = "string example";

The identifier can be referenced at any point in the user’s code where a literal of that type is legal.

### Mapping for basic data types

The basic data types have the mappings shown in the table below. Integer types use the `C99` fixed size integer types as provided by the `stdint.h` standard header. Users do not have to include this header: the template mapping generation procedure output the appropriate `#include` directive along with the mappings for the integer types.

Table 1. Basic data types mappings in C
IDL | C
--- | ---
`boolean` | `bool`
`unsigned short` | `uint16_t`
`short` | `int16_t`
`unsigned long` | `uint32_t`
`long` | `int32_t`
`unsigned long long` | `uint64_t`
`long long` | `int64_t`
`float` | `float`
`double` | `double`
`char` | `int8_t`
`octet` | `uint8_t`
`any` | type any not implemented yet

### Mapping for enumerated types

The C mapping of an IDL `enum` type is an unsigned, 32 bits wide integer. Each enumerator in an enum is defined in an anonymous `enum` with an appropriate unsigned integer value conforming to the ordering constraints.

For instance, the following IDL:

          module m {
            enum e {
              value1,
              value2
            };
          };

would map, according to the scoped names rules, into

          typedef uint32_t m_e;
          enum {
            m_value1 = 0
            m_value2 = 1
          };

### Mapping for strings

`genom3` IDL bounded strings are mapped to nul terminated character arrays (i.e., C strings). Unbounded strings are mapped to a pointer on such a character array.

For instance, the following OMG IDL declarations:

          typedef string unbounded;
          typedef string\<16> bounded;

would map into

          typedef char *unbounded;
          typedef char bounded[16];

### Mapping for arrays

`genom3` IDL arrays map directly to C arrays. All array indices run from 0 to `size-1`.

For instance, the following IDL:

          typedef long array[4][16];

would map into

          typedef int32_t array[4][16];

### Mapping for structure types

`genom3` IDL structures map directly onto C `struct`s. Note that these structures may potentially include padding.

For instance, the following IDL:

          struct s {
             long a;
             long b;
          };

would map into

          typedef struct {
             int32_t a;
             int32_t b;
          } s;

### Mapping for union types

`genom3` IDL unions map onto C `struct`. The discriminator in the enum is referred to as `_d`, the union itself is referred to as `_u`.

For instance, the following IDL:

          union u switch(long) {
             case 1: long a;
             case 2: float b;
             default: char c;
          };

would map into

          typedef struct {
             int32_t _d;
             union {
                int32_t a;
                float b;
                char c;
             } _u;
          } u;

### Mapping for sequence types

`genom3` IDL sequences mapping differ slightly for bounded or unbouded variations of the sequence. Both types maps onto a C `struct`, with a `_maximum`, `_length` and `_buffer` members.

For unbounded sequences, `buffer` points to a buffer of at most `_maximum` elements and containing `_length` valid elements. An additional member ` _release` is a function pointer that can be used to release the storage associated to the ` _buffer` and reallocate it. It is the responsibility of the user to maintain the consistency between those members.

For bounded sequences, ` buffer` is an array of at most `_maximum` elements and containing `_length` valid elements. Since `_buffer` is an array, no memory management is necessary for this data type.

For instance, the following IDL:

          typedef sequence<long> unbounded;
          typedef sequence<long,16> bounded;

would map into

          typedef struct {
             uint32_t _maximum, _length;
             int32_t *_buffer;
             void (*_release)(void *_buffer);
          } unbounded;

          typedef struct {
             uint32_t _maximum, _length;
             int32_t _buffer[16];
          } bounded;

A helper function for reserving space in unbounded sequences is available. It is defined as follow:

          int genom_sequence_reserve(sequence_type *s, uint32_t length);

where `sequence_type` is the actual type name of the sequence.

Given a pointer `s` on a valid, unbouded sequence, `genom_sequence_reserve` will allocate storage for up to `length` elements. If needed, the previous storage is first released using the function pointed to by `_release`. If the sequence already had enough space available, `genom_sequence_reserve` does nothing. New storage is allocated using `malloc()`, and the `_release` member is updated to point on the `free()` function. The `_maximum` member is updated to reflect the new maximum number of elements that the sequence may hold. Finally, the function returns 0 on success, or -1 if no memory could be allocated (in this case, the global variable `errno` is updated with the value `ENOMEM`).

Beware that `genom_sequence_reserve` is able to shrink a sequence if the `length` parameter happens to be smaller than the current length of a sequence. In this case, any elements previously stored beyond the new effective length will be definitely lost. The `_length` member will be updated accordingly.

The use of this function to reserve space in a sequence is purely optional. Any storage allocation strategy can be used, provided it maintains the consistency between the `_maximum`, `_release` and `_buffer` elements.

### Mapping for optional types

`genom3` IDL optional types map onto C `struct` with a `_present` and `_value` members.

For instance, the following IDL:

          typedef optional< long > opt;

would map into

          typedef struct {
             bool _present;
             int32_t _value;
          } opt;

The `_present` member, when `true`, indicates the presence of valid data in `_value`. When `_present` is `false`, the `_value` should be ignored.

### Mapping for port types

Simple ports map onto an object-like C `struct` with a `data()` and `read()` or `write()` function members. The `data()` function takes no parameter and returns a pointer on the current port data. Input ports may refresh their data by invoking the `read()` method, while output ports may publish new data by invoking the `write()` method. Both `read()` and `write()` return `genom_ok` on success, or a `genom_event` exception representing an error code.

Ports defined with the `multiple` flag map onto a similar `struct`, with the difference that `data()`, `read()` and `write()` methods take an additional string (`const char *`) parameter representing the port element name. Multiple output ports have two additional `open()` and `close()` members (also accepting a single string parameter) that dynamically create or destroy ports.

For instance, the following IDL:

          port in double in_port;
          port multiple in double multi_in_port;
          port out double out_port;
          port multiple out double multi_out_port;

would map into

          typedef struct {
             double * (*data)(genom_context self);
             genom_event (*read)(genom_context self);
          } in_port;

          typedef struct {
             double * (*data)(const char *id, genom_context self);
             genom_event (*read)(const char *id, genom_context self);
          } multi_in_port;

          typedef struct {
             double * (*data)(genom_context self);
             genom_event (*write)(genom_context self);
          } out_port;

          typedef struct {
             double * (*data)(const char *id, genom_context self);
             genom_event (*write)(const char *id, genom_context self);
             genom_event (*open)(const char *id, genom_context self);
             genom_event (*close)(const char *id, genom_context self);
          } multi_out_port;

### Mapping for remote services

Remote objects map onto an object-like C `struct` providing a `call()` method. `call()` takes the same parameters as the corresponing service definition and returns `genom_ok` on success, or a `genom_event` exception representing an error code.

For instance, the following IDL:

          interface i {
            function f(in long i, out double o);
          };

          component c {
            uses i;
          };

would map into

          typedef struct c_f {
            genom_event (*call)(uint32_t i, double *o, genom_context self);
          } c_f;

The remote service is invoked in a synchronous manner.

### Mapping for native types

`genom3` IDL native types map to a C `struct`. The mapping provides only a forward declaration, and the user has to provide the actual definition.

For instance, the following IDL:

          native opaque;

would map into

          typedef struct opaque opaque;

The definition of the structure body is free, and will typically use native C types that cannot be described in IDL. When used as a parameter of a function, a native type will be passed around as a pointer on the structure data. Memory management associated with that pointer must be handled by the user.

### Mapping for exceptions

Each defined exception type is defined as a `struct` tag and a `typedef` with the C global name for the exception suffixed by `_detail`. An identifier for the exception is also defined, as is a type-specific function for raising the exception. For example:

          exception foo {
            long dummy;
          };

yields the following C declarations:

          genom_event foo_id = <unique identifier for exception>;

          typedef struct foo_detail {
            uint32_t dummy;
          } foo_detail;

          genom_event foo(const foo_detail *detail, genom_context self);

The identifier for the exception uniquely identifies this exception type, so that any data of type `genom_event` can be compared to an exception id with the `==` operator.

The function throwing the exception returns a `genom_event` that should be used as the return value of a codel. It makes a copy of the exception details.

Since exceptions are allowed to have no members, but C structs must have at least one member, exceptions with no members map to the C `void` type and the type-specific throw function takes no argument.