C Language Support

Files Generated

For the C language, five files are generated by ifgen from the component interface file:

  • client interface header file (interface.h) - C definitions for the interface. This file should be included by any C source files that want to use the interface.
  • server header file (server.h) - C definitions for the server interface. There is some duplication between this file and the interface file, but it also contains definitions that are not part of the public client interface. This file should be included by any C source files that want to implement the interface.
  • local header file (local.h) - local header file provides common definitions for the client and server implementations. This file should only be included by the client and server implementation files.
  • client implementation file (client.c) - implements all of the interface functions. These functions handle the details of sending messages to the server, and processing the responses.
  • server implementation file (server.c) - implements handlers for all the interface functions. These handlers receive the message from the client side, call the corresponding real implementation of the function, and generate any responses back to the client side.
Note
Client and server implementation files are provided to support client/server IPC implementations.

Handler Parameters in C

A handler parameter can be used in both events and regular functions. This is how a handler parameter for a function in an interface file is mapped to actual C definitions (based on the API File Sample Output).

FUNCTION int32 UseCallback
(
uint32 someParm IN,
TestAHandler handler
);

This results in the function definition:

int32_t UseCallback
(
uint32_t someParm,
TestAHandlerFunc_t handlerPtr,
void* contextPtr
);

A contextPtr parameter has been automatically added to the definition of the function. This contextPtr will be passed back to the specified handler function when it is invoked. If the contextPtr is not needed, then NULL can be used.

Events in C

This is how an event in an interface file is mapped to actual C definitions (based on the API File Sample Output).

EVENT TestA
(
uint32 data,
TestAHandler myHandler
);

This results in one type definition and two function definitions:

typedef struct TestAHandler* TestAHandlerRef_t;
 
TestAHandlerRef_t AddTestAHandler
(
uint32_t data,
TestAHandlerFunc_t myHandlerPtr,
void* contextPtr
);
 
void RemoveTestAHandler
(
TestAHandlerRef_t addHandlerRef
);

The parameters from the event definition are used in the ADD_HANDLER function. This is used to register the given handler for events. The REMOVE_HANDLER function does not take any additional parameters, other than the reference returned by the ADD_HANDLER function.

A contextPtr parameter has been automatically added to the definition of the ADD_HANDLER function. The contextPtr passed to the ADD_HANDLER function, will be passed back to the registered handler function. If the contextPtr is not needed, then NULL can be used.

Handlers in C

This is how a handler in an interface file is mapped to actual C definitions (based on the API File Sample Output):

HANDLER TestAHandler
(
int32 x
);

This results in a type definition for the handler function pointer:

typedef void (*TestAHandlerFunc_t)
(
int32_t x,
void* contextPtr
);

A contextPtr parameter has been automatically added to the definition of the handler function pointer. The contextPtr passed to a regular function or ADD_HANDLER function (see section Handler Parameters in C or Events in C, respectively) will be passed back to the handler function. If the contextPtr is not needed, then NULL can be used.

Client Specific Functions

The API files provide the following client specific functions:

  • TryConnectService()
  • ConnectService()
  • DisconnectService()
  • SetServerDisconnectHandler()

If you wish to check that the API that you are connecting to is available or not use TryConnectService() first. This function must be called first, and while the ConnectServce() function is called automatically for the main thread, all other threads should use this function to make sure that they can successfully connect before they open a connection.

To use a service, a client must connect to the server using ConnectService(). This connection is only created for the current thread. Other threads must also call ConnectService() to use the service.

For the main thread, ConnectService() is usually automatically called when the client app is initialized. Disable this by using the .cdef provides [manual-start].

If a client app uses multiple services, multiple ConnectService() functions need to be called; each function uses an appropriate prefix to distinguish these clients. If multiple client apps are used, each app must be initialized separately, using the appropriate ConnectService() function.

The DisconnectService() function closes a connection to the server. It only closes the current thread connection; it must be called separately for each thread using a service. If a thread wants to use the service again, it must call ConnectService() to re-connect. When the app exits, all connections for all threads are automatically closed. A thread only needs to use DisconnectService() when it wants to disconnect from a service while the app is still running (e.g., no longer needs the service so it can conserve resources). To handle disconnections use SetServerDisconnectHandler() to exit with LE_FATAL. If an app wants to continue without exiting call longjmp() from inside the handler.

Server Specific Functions

These are server Wspecific functions:

le_msg_ServiceRef_t GetServiceRef
(
void
);
 
le_msg_SessionRef_t GetClientSessionRef
(
void
);
 
void AdvertiseService
(
void
);

To provide a service, the server must advertise the service to any interested clients using AdvertiseService(). This is usually automatically called during the initialization of the server daemon. This can be disabled using the .cdef provides [manual-start].

If a server provides multiple services, multiple AdvertiseService() functions need to be called; each function will have an appropriate prefix to distinguish each service.

The GetServiceRef() function is used to get the server session reference for the current service. It's required if the server uses any of the server-specific Low-level Messaging API functions for this service.

For example, le_msg_AddServiceCloseHandler can be used by the server to register a close handler whenever a client closes its connection. This may be needed to cleanup client specific data maintained by the server.

GetClientSessionRef() function is used to get the client session reference for the current service. This client session is only valid while executing the server-side function that implements an interface function. Once this server-side function returns, the client session can no longer be retrieved. GetClientSessionRef() is needed if the server wants to call any of the client-specific Low-level Messaging API functions for this service.

For example, le_msg_GetClientUserId() can be used by the server to determine the UserId of the client using the service, which allows the server to perform any necessary UserId based authentication.

Asynchronous Server

There are two alternatives to implement the server-side functionality.

The default case is where each server-side function has the same interface as the client-side function. The server-side function takes the IN parameters, and returns the OUT parameters and function result when the function exits.

In the async-server case, the server-side function doesn't necessarily return the OUT parameters and function result when it exits. Instead, there's a separate Respond function for each server-side function. The OUT parameters and function result are returned by passing these values to the Respond function. The Respond function can be called at any time, normally after the server-side function has exited.

Regardless of how the server-side functions are implemented, the client-side function waits until the OUT parameters and function result are returned.

The async-server functionality is not enabled by default. Enable it by using the .cdef provides [async].

Sending File Descriptors

If a file descriptor is sent over the Legato IPC, the underlying messaging infrastructure would close the file descriptor. Therefore, after the file descriptor is passed as an "IN parameter" to a function that uses Legato IPC, the file descriptor should not be explicitly closed. If the file descriptor is needed after calling such function, it should be duplicated before being passed to the function.

See the "file" type in Type Support.

See Sending File Descriptors for more detail.

API File Sample Output

Here's the generated client interface header file for the defn.api file from API File Sample Output

/*
 * ====================== WARNING ======================
 *
 * THE CONTENTS OF THIS FILE HAVE BEEN AUTO-GENERATED.
 * DO NOT MODIFY IN ANY WAY.
 *
 * ====================== WARNING ======================
 */


#ifndef DEFN_H_INCLUDE_GUARD
#define DEFN_H_INCLUDE_GUARD


#include "legato.h"


//--------------------------------------------------------------------------------------------------
/**
 * Example of nested .api file
 */
//--------------------------------------------------------------------------------------------------
#define DEFN_SIX 6


#endif // DEFN_H_INCLUDE_GUARD

Here's the generated client interface header file for the common.api file from API File Sample Output

/*
 * ====================== WARNING ======================
 *
 * THE CONTENTS OF THIS FILE HAVE BEEN AUTO-GENERATED.
 * DO NOT MODIFY IN ANY WAY.
 *
 * ====================== WARNING ======================
 */

/**
 * Common definitions potentially used across multiple .api files
 */

#ifndef COMMON_H_INCLUDE_GUARD
#define COMMON_H_INCLUDE_GUARD


#include "legato.h"

// Interface specific includes
#include "defn_interface.h"



//--------------------------------------------------------------------------------------------------
/**
 * Definition example
 */
//--------------------------------------------------------------------------------------------------
#define COMMON_FOUR 4


//--------------------------------------------------------------------------------------------------
/**
 * Example of using previously DEFINEd symbol within an imported file.
 */
//--------------------------------------------------------------------------------------------------
#define COMMON_TEN 10


//--------------------------------------------------------------------------------------------------
/**
 * Reference example
 */
//--------------------------------------------------------------------------------------------------
typedef struct common_OpaqueReference* common_OpaqueReferenceRef_t;


//--------------------------------------------------------------------------------------------------
/**
 * ENUM example
 */
//--------------------------------------------------------------------------------------------------
typedef enum
{
    COMMON_ZERO,
        ///< first enum

    COMMON_ONE,
        ///< second enum

    COMMON_TWO,
        ///< third enum

    COMMON_THREE
        ///< fourth enum
}
common_EnumExample_t;


//--------------------------------------------------------------------------------------------------
/**
 * BITMASK example
 */
//--------------------------------------------------------------------------------------------------
typedef enum
{
    COMMON_BIT0 = 0x1,
        ///< first

    COMMON_BIT1 = 0x2,
        ///< second

    COMMON_BIT2 = 0x4
        ///< third
}
common_BitMaskExample_t;


#endif // COMMON_H_INCLUDE_GUARD

Here's the generated client interface header file for the example.api file from API File Sample Output

/*
 * ====================== WARNING ======================
 *
 * THE CONTENTS OF THIS FILE HAVE BEEN AUTO-GENERATED.
 * DO NOT MODIFY IN ANY WAY.
 *
 * ====================== WARNING ======================
 */

/**
 * Example API file
 */

#ifndef EXAMPLE_H_INCLUDE_GUARD
#define EXAMPLE_H_INCLUDE_GUARD


#include "legato.h"

// Interface specific includes
#include "defn_interface.h"
#include "common_interface.h"


//--------------------------------------------------------------------------------------------------
/**
 * Connect the client to the service
 */
//--------------------------------------------------------------------------------------------------
void example_ConnectService
(
    void
);

//--------------------------------------------------------------------------------------------------
/**
 * Disconnect the client from the service
 */
//--------------------------------------------------------------------------------------------------
void example_DisconnectService
(
    void
);


//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
#define EXAMPLE_TEN 10


//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
#define EXAMPLE_TWENTY 20


//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
#define EXAMPLE_SOME_STRING "some string"


//--------------------------------------------------------------------------------------------------
/**
 * Reference type used by Add/Remove functions for EVENT 'example_TestA'
 */
//--------------------------------------------------------------------------------------------------
typedef struct example_TestAHandler* example_TestAHandlerRef_t;


//--------------------------------------------------------------------------------------------------
/**
 * Handler definition
 *
 * @param x
 *        First parameter for the handler
 *        Second comment line
 * @param contextPtr
 */
//--------------------------------------------------------------------------------------------------
typedef void (*example_TestAHandlerFunc_t)
(
    int32_t x,
    void* contextPtr
);

//--------------------------------------------------------------------------------------------------
/**
 * Add handler function for EVENT 'example_TestA'
 *
 * This event provides an example of an EVENT definition
 */
//--------------------------------------------------------------------------------------------------
example_TestAHandlerRef_t example_AddTestAHandler
(
    uint32_t data,
        ///< [IN]
        ///< Used when registering the handler i.e. it is
        ///< passed into the generated ADD_HANDLER function.

    example_TestAHandlerFunc_t handlerPtr,
        ///< [IN]

    void* contextPtr
        ///< [IN]
);

//--------------------------------------------------------------------------------------------------
/**
 * Remove handler function for EVENT 'example_TestA'
 */
//--------------------------------------------------------------------------------------------------
void example_RemoveTestAHandler
(
    example_TestAHandlerRef_t addHandlerRef
        ///< [IN]
);

//--------------------------------------------------------------------------------------------------
/**
 * Function takes all the possible kinds of parameters, but returns nothing
 */
//--------------------------------------------------------------------------------------------------
void example_AllParameters
(
    common_EnumExample_t a,
        ///< [IN]
        ///< first one-line comment
        ///< second one-line comment

    uint32_t* bPtr,
        ///< [OUT]

    const uint32_t* dataPtr,
        ///< [IN]

    size_t dataNumElements,
        ///< [IN]

    uint32_t* outputPtr,
        ///< [OUT]
        ///< some more comments here
        ///< and some comments here as well

    size_t* outputNumElementsPtr,
        ///< [INOUT]

    const char* label,
        ///< [IN]

    char* response,
        ///< [OUT]
        ///< comments on final parameter, first line
        ///< and more comments

    size_t responseNumElements
        ///< [IN]
);

//--------------------------------------------------------------------------------------------------
/**
 * Test file descriptors as IN and OUT parameters
 */
//--------------------------------------------------------------------------------------------------
void example_FileTest
(
    int dataFile,
        ///< [IN]
        ///< file descriptor as IN parameter

    int* dataOutPtr
        ///< [OUT]
        ///< file descriptor as OUT parameter
);

//--------------------------------------------------------------------------------------------------
/**
 * Function that takes a handler parameter
 */
//--------------------------------------------------------------------------------------------------
int32_t example_UseCallback
(
    uint32_t someParm,
        ///< [IN]

    example_TestAHandlerFunc_t handlerPtr,
        ///< [IN]

    void* contextPtr
        ///< [IN]
);


#endif // EXAMPLE_H_INCLUDE_GUARD