-
Notifications
You must be signed in to change notification settings - Fork 243
IDL Reference
Follow the same rules as in C/C++. Identifiers must start with an alphabetic character or underscore, and may include alphanumeric characters and underscores.
The following literal values are supported:
- Integer: Decimal (base 10) is the default. Hex values are prefixed with '0x' and binary values are prefixed with '0b'. Integer literals may be suffixed with 'u', 'ul', or 'ull', and are case-insensitive.
- Floating point: A decimal point prefixed and/or suffixed by decimal character sequences. The decimal point is required to form a floating point value. The optional exponent follows with no extra whitespace, and consists of 'e' or 'E', optional +/-, and a decimal sequence.
- Strings: are contained in double quotes. The usual escape sequences that begun with a backslash are supported, as well as hex character escapes such as "\x7d". Multiple consecutive string literals are accepted anywhere a string is valid in the grammar, and are concatenated into a single string.
All whitespace is ignored in the input file.
An expression is accepted in any place where an integer or floating point value is required.
| Operator | Function | Operator | Function |
|---|---|---|---|
| + | addition | ^ | binary XOR |
| - | subtraction | << | left shift |
| * | multiplication | >> | right shift |
| / | division | ||
| % | modulo | + | no-op (unary operator) |
| & | binary AND | - | negate (unary operator) |
| | | binary OR | ~ | binary inverse (unary operator) |
The usual precedence rules apply. Sub-expressions may be placed in parentheses ( ) to override normal precedence rules.
Another IDL file can be included in the parse by using the import keyword.
Prototype:
import stringThe string contains just the name, or the name-contained file path to the IDL file. The file path is relative to the base IDL file, or the file path could be relative to the given path to the eRPC generator.
Annotations provide a way to inform the eRPC generator about specific requests for some parts of the code.
- Unrecognized annotations are ignored by code generators.
- Each annotation starts with the “
@” character. - It is followed by the annotation name; for example,
@external.
This is enough for one type of annotation (when you want to only inform the eRPC generator about a specific attribute of the code). The second type of annotation is extended using the “( )” parentheses. A specific parameter can be placed inside parentheses. This can be a number, text, or other things.
When the annotation @error_return is set, then this _value _ is used for the return value from the client-side called function, when the error will occur inside.
The @external annotation allows you to reference a type in an IDL file that already exists in another header file. In order for this to work, the type must be identically defined in the IDL file.
For example, if you have a typedef in a header file (typedef uint32_t erpc_status_t), and want to use this typedef, then define the type in your IDL file using the @external annotation: @external type erpc_status_t = uint32.
- This allows erpcgen to perform type checking when parsing the IDL file, but it will not generate any code for the type.
- This prevents duplicate definitions of the same type.
- This should work for all type definitions in an IDL file (struct, const, enum, typedef).
Interfaces can be grouped and stored in their set of output files via using @group annotation. String represent group name, which is used for output files. The file names look like: <fileName>_<groupName>.
| Filename | Description |
|---|---|
| <outputFileName>_<groupName>.h | Common header file for group |
| <outputFileName>_<groupName>_client.cpp | Client shim implementation for group |
| <outputFileName>_<groupName>_server.h | Server side header for group |
| <outputFileName>_<groupName>_server.cpp | Server shim implementation for group |
Annotation @id set interface/function id number. The number must be unique for each interface/function.
The string value represents the header file to include.
This annotation set reference for length variable. This can be either a number or variable. This prevents from wrapping list and binary data types to structure. Instead of structure, these data types are present as pointer to the element type. The length of pointer variable depends on value.
Generated files have a default-enabled generating error checking code for catching errors from bad allocations. For disabling generating errors checking code for bad allocation just add @no_alloc_errors.
Generated files have a default-enabled generating error checking code for catching errors from codec functions. For disabling generating errors checking code from codec, just add @no_infra_errors annotation.
Annotation @max_length sets the maximum size of the string value. This size need be allocated by the client. This annotation simply informs the server how big of a space should be allocated to working with this variable. For example, when the variable is used as the inout type and the server needs to send back a bigger string, then the client sends it to them.
Annotation @nullable informs shim code that variables can be set to NULL. Without this annotation, the variable (passed by client, or returned from the server function implementation) cannot be NULL.
String value that represents the output path. The output directory can also be set as a command line option for the eRPC generator. When @outputDir(string) and the eRPC generator command line option are both used, then the path from the IDL file is appended to the path given to the eRPC generator.
Annotation @retain prevents the server to call freeing functions on variables, which are used in server function implementation.
All type declarations (for example typedef and struct) will be moved into the defined header file. This prevents duplicate definition errors when using @group.
The optional program statement is used to specify the name of the input as a whole. This name is used for the output file name.
program some_program_nameThe program statement does not cause any code to generate by itself. It is used both for documentation purposes and as an anchor point to which annotations may be attached. Annotations must be placed before the program statement.
@outputDir(string)
@include(string)
@no_infra_errors
@no_alloc_errors
@types_header
The interface contains one or more functions, which are called on the client side. The implementations are written on the server side.
Prototype:
interface _interfaceName_ {}Interfaces also generate one more special function. Because generated functions are written as C++ functions (they use classes), C-users generate C-function create_<interfaceName>_service(). This function should be the call function.
Supported annotations are used before a interface declaration.
@group(string)
@id(number)
Functions are called by the client, and the implementations are written on the server side. There are two supported IDL function declarations:
oneway function()The oneway function should be used when no return message is expected.
function() -> returnDataTypeThis function should be used when a return message is expected (for example, the returning value). The difference between using the oneway and void is that the void function waits for a response from the server side.
Attributes are placed inside parentheses ( ), similar to how it is done in C-functions (using the supported IDL data types).
Supported annotations are used before a function declaration.
@id(number)
@error_return(value)
Parameters annotations are set after parameter name. Supported annotations are described for each data type in chapters bellow. Prototype:
oneway function(string a @max_length(10), list<int32> b @length(c), int32 c)These are the built-in, atomic, scalar, complex types supported by erpcgen. These types are mostly self-explanatory.
@external
The IDL type representation for this is: type _aliasName_ = _originalType_
| IDL definition | C definition |
|---|---|
| type aliasName = int32 | typedef int32_t aliasName; |
| type aliasName = list | typedef list_0 aliasName; |
| type aliasName = int32[20] | typedef int32_t aliasName[20]; |
The built-in atomic types convert to standard integer types. The string type converts directly to a char*.
| IDL definition | C definition | IDL definition | C definition |
|---|---|---|---|
| bool | bool | uint8 | uint8_t |
| int8 | int8_t | uint16 | uint16_t |
| int16 | int16_t | uint32 | uint32_t |
| int32 | int32_t | uint64 | uint64_t |
| int64 | int64_t | float | float |
| string | char* | double | double |
The string length is determined by the terminating null Byte, which means that no null characters may be included in the middle of the string. The major impact of this is that UTF-8 is not fully supported. If the arbitrary UTF-8 text is intended to be transferred, then the binary type must be used.
@max_length(value)
@nullable
@retain
As default the binary type generates a C-type equivalent to list<uint8>. The differences between binary type and list type are:
- Binary data type is handled far more efficiently when serializing and deserializing.
- Binary type has different names of structure members.
typedef struct binary_t binary_t;
struct binary_t {
int8_t *data;
uint32_t dataLength;
};@retain
@length(value)
@max_length(value)
Enumerations in the IDL input generate matching enumeration definitions in C. The only difference is that constant expressions are evaluated in the output. Additionally, a typedef with a matching name is generated.
enum enumColor {
red,
green = 10,
blue = 2 * 10
}typedef enum enumColor {
red,
green = 10,
blue = 20
} enumColor;Structures are created with an implicit typedef for the name provided in the IDL file.
struct A {
int32 a
float b
}typedef struct A A;
struct A {
int32_t a;
float b;
};Two helper functions are generated for encoding and decoding structures. The prototypes for such functions are:
int32_t read_<struct_typename>_struct(erpc::Codec * in, <struct_typename> * data);int32_t write_<struct_typename>_struct(erpc::Codec * out, <struct_typename> * data);
For all functions that either return a structure or pass a structure as a parameter, those functions expect a pointer to the structure type.
@retain
@nullable
A discriminated union is simply a struct with one or more scalar types, one or more unions, and any other necessary data members for the struct. A discriminated union allows a user to create a kind of variant object, thereby allowing a user to send or return data of different types wrapped in a common struct definition. The definition of a discriminated union in the IDL file uses a switch-statement-like syntax to define which union member is valid for the corresponding discriminator value.
enum { A, B, C, D, E, F }
struct A {
int32 discriminator
union(discriminator)
{
case A:
int32 a
case B:
float b
case C:
case D, E:
list<int32> c
case F:
int32 x
int32 y
default:
int32 z
} data
}typedef struct A A;
struct A {
int32_t discriminator;
union
{
int32_t a;
float b;
list_0 c;
struct {
int32_t x;
int32_t y;
};
int32_t z;
} data;
};Discriminated unions have a few special features:
- As shown for cases C, D, and E, you can assign the same union member to multiple discriminator values, either by allowing case definitions to fall through, or by providing a list of discriminator values within a single case.
- Case F shows that discriminated unions also allow generation of anonymous structs within the union.
- There is also the ‘default’ case, which is invoked if the discriminator value is set to something not defined explicitly by one of the union cases within the IDL.
@retain
@nullable
The IDL type representation for this type is list<typename>.
Because the C language does not have a built-in list type, the generator must synthesize one (a list type) when generating code. This is done by combining an array pointer with a list element count in a structure. Each unique list type specified in the IDL causes a new structure type to be defined.
The conversion is very straightforward. For list<int32>, this code is generated:
struct list_0_t {
int32_t * elements;
uint32_t elementsCount;
};Type definitions for the list structures are generated in a separate forward declarations section of the output file.
For simplicity, the names of the synthesized list structures are currently set to "list_x", where x is a unique integer. It is recommended to create a type alias to define a user-friendly name for the list structure. The issue is that the list structure names that include an element type description could potentially become excessively long.
@retain
@length
@max_length
The IDL type representation for this type is typeName[N] variableName, where N is an integer constant expression.
| IDL definition | C definition |
|---|---|
| int32[10] variable | int32_t variable[10]; |
| int32[10][6] variable | int32_t variable[10][6]; |
functionName(int [8][10] a) -> int [8][4]int * (functionName(const int32_t (*a)[8][10]))[8][4];One “*” character is added because arrays and structures are sent as pointers to the parameter. This is shown in the routing section below. If a type alias is used, then the return value is a pointer to the typedef name.
- erpc:
type INTTYPE = int[8][10]
functionName(INTTYPE a) -> INTTYPE;- output:
INTTYPE* functionName(INTTYPE *a);@retain
The IDL supports declarations of constants. These constants are replicated in the generated output. Constants are defined with the const keyword. They look just like constant global variables in C, and include a type and name (in that order).
String constant prototype: const string kVersion = stringValue
Supported keywords for parameter directions are: in, out, and inout. These keywords are used to specify the parameter direction type. The direction keywords are placed before the parameter type. If the direction for a parameter is not specified, then it is the in direction.
For example: void function(out int32 param)
The next figure shows who is sending and who is receiving the parameter value.

How memory allocations are provided depends on the type of application:
- On client-side applications: all memory allocations have to be provided by user code.
- On server-side applications: all memory allocations are provided by server shim code. The user cannot change the memory allocation provided by the shim code.
The next table shows how data types are translated from IDL to C as function parameters.
| Data type | in | out | inout | Returns |
|---|---|---|---|---|
| Built-in types | int32 param | int32 *param | int32 *param | int32 |
| string | char *param | char *param | char *param | Not supported |
| enum | B param | B *param | B *param | B |
| struct | const A *param | A *param | A *param | Not supported |
| list | const list_0 *param | const list_0 *param | list_0 *param | Not supported |
| array | const int32 (*param)[5] | int32 (*param)[5] | int32 (*param)[5] | Not supported |
If a type definition is used, then all pointers are generated as shown in the two previous tables. Instead of these types, the type definition name is used.
Sometimes variable need have set annotation to work for some cases. For example out string need set @max_length annotation to inform server how big memory space need allocate for string. This size should client allocate on client side and it will be filled by shim code.
- On the client side: All memory space has to be allocated and provided by user code. Shim code only reads from or writes into this memory space.
- On the server side: All memory space is allocated and provided by shim code. User code only reads from or writes into this memory space.
When the client uses a list data type, the code has to allocate space for the elements and put the number of elements in elementsCount. The client can set elementsCount to “0”.
There are two types of comments:
Are //sometext or /* sometext */. These can be used everywhere within the IDL file, but these comments are not copied into the generated output file.
Are copied into the generated output header file. Doxygen comments can be placed before a declaration, and look like the following:
/*! sometext */
/** sometext */
/// sometext
//! sometextTrailing Doxygen comments are placed after:
- member declaration
- name for structure
- name for enumeration
- name for interface
- constant declaration
- function declaration
and they look like the following:
/*!< sometext */
/**< sometext */
///< sometext
//!< sometext