When building a solution, the NK compiler uses the EDL-, CDL- and IDL descriptions to generate a set of special methods and types that simplify the creation, forwarding, receipt and processing of IPC messages.
Examine the static description of the Server
entity from the echo example. This description consists of three files: Server.edl
, Ping.cdl
and Ping.idl
:
Server.edl
/* Server entity description. */
entity Server
/* pingComp is a named instance of the Ping component. */
components {
pingComp: Ping
}
Ping.cdl
/* Ping component description. */
component Ping
* pingImpl is the Ping interface implementation. */
interfaces {
pingImpl: Ping
}
Ping.idl
/* Ping interface description. */
package Ping
interface {
Ping(in UInt32 value, out UInt32 result);
}
These files will be used to generate the files named Server.edl.h
, Ping.cdl.h
, and Ping.idl.h
, which contain the following methods and types:
Methods and types that are common to the client and server
In our example, one abstract interface (Ping
) will be generated:
struct Ping_ops {
nk_err_t (*Ping)(struct Ping *,
const struct Ping_req *,
const struct nk_arena *,
struct Ping_res *,
struct nk_arena *); };
struct Ping {
const struct Ping_ops *ops;
};
When calling an interface method, corresponding values of the RIID and MID are automatically inserted into the request, after which the nk_transport_call()
function is called.
In our example, a single Ping_Ping
interface method will be generated:
nk_err_t Ping_Ping(struct Ping *,
const struct Ping_Ping_req *,
const struct nk_arena *,
struct Ping_Ping_res *,
struct nk_arena *);
Methods and types used only on the client
A proxy object is used as an argument in an interface method. In our example, a single Ping_proxy
proxy object type will be generated:
struct Ping_proxy {
struct Ping base;
struct nk_transport *transport;
nk_iid_t iid;
};
In our example, the single initializing function Ping_proxy_init
will be generated:
void Ping_proxy_init(struct Ping_proxy *, struct nk_transport *, nk_iid_t);
In our example, two such types will be generated: Ping_Ping_req
(for a request) and Ping_Ping_res
(for a response).
struct Ping_Ping_req {
struct nk_message base_;
nk_uint32_t value;
};
struct Ping_Ping_res {
struct nk_message base_;
nk_uint32_t result;
};
Methods and types used only on the server
If there are embedded components, this type also contains their instances, and the initializing function takes their corresponding initialized structures. Therefore, if embedded components are present, their initialization must begin with the most deeply embedded component.
In our example, the Ping_component
structure and Ping_component_init
function will be generated:
struct Ping_component {
struct Ping *pingImpl;
};
void Ping_component_init(struct Ping_component *, struct Ping *);
In our example, the Server_entity
structure and Server_entity_init
function will be generated:
struct Server_entity {
struct Ping_component *pingComp;
};
void Server_entity_init(struct Server_entity *, struct Ping_component *);
In our example, two such types will be generated: Ping_req
(for a request) and Ping_res
(for a response).
union Ping_req {
struct nk_message base_;
struct Ping_Ping_req Ping;
};
union Ping_res {
struct nk_message base_;
struct Ping_Ping_res Ping;
};
If embedded components are present, these types also contain structures of the constant part of a message for any method of any interface whose implementations are included in all embedded components.
In our example, two such types will be generated: Ping_component_req
(for a request) and Ping_component_res
(for a response).
union Ping_component_req {
struct nk_message base_;
union Ping_req pingImpl;
};
union Ping_component_res {
struct nk_message base_;
union Ping_res pingImpl;
};
If embedded components are present, these types also contain structures of the constant part of a message for any method of any interface whose implementations are included in all embedded components.
In our example, two such types will be generated: Server_entity_req
(for a request) and Server_entity_res
(for a response).
union Server_entity_req {
struct nk_message base_;
union Ping_req pingComp_pingImpl;
};
union Server_entity_res {
struct nk_message base_;
union Ping_res pingComp_pingImpl;
};
Dispatchers analyze the received request (the RIID and MID values), call the implementation of the corresponding method, and then save the response in the buffer. In our example, three dispatchers will be generated: Ping_dispatch
, Ping_component_dispatch
, and Server_entity_dispatch
.
The entity dispatcher handles the request and calls the methods implemented by this entity. If the request contains an incorrect RIID (for example, an RIID for a different interface implementation that this entity does not have) or an incorrect MID, the dispatcher returns NK_EOK
or NK_ENOENT
.
nk_err_t Server_entity_dispatch(struct Server_entity *,
const union Server_entity_req *,
const struct nk_arena *,
union Server_entity_res *,
struct nk_arena *);
In special cases, you can use dispatchers of the interface and the component. They take an additional argument: interface implementation ID (nk_iid_t
). The request will be handled only if the passed argument and RIID from the request match, and if the MID is correct. Otherwise, the dispatchers return NK_EOK
or NK_ENOENT
.
nk_err_t Ping_dispatch(struct Ping *,
nk_iid_t,
const union Ping_req *,
const struct nk_arena *,
union Ping_res *,
struct nk_arena *);
nk_err_t Ping_component_dispatch(struct Ping_component *,
nk_iid_t,
const union Ping_component_req *,
const struct nk_arena *,
union Ping_component_res *,
struct nk_arena *);