7.3.2. C++ API Reference
The C++ API is mainly a convenience wrapper around the C Interface, which provides the implementation. In case that you need more details, links to the C counter parts are provided where useful.
7.3.2.1. Required Headers
To use the LN C++ client library, you have to include its headers into your C++ source file like this:
#include <ln/ln.h>
7.3.2.2. Thread-Safety
The API functions can be classified into two categories:
Functions and constructors which set up connections and configure the client, like the
ln::clientconstructor, or theln::client::subscribe()method.Functions and constructors which do communication.
The first group of functions should only be used in a single thread. The second group of functions can be used from multiple threads and are thread-safe.
7.3.2.3. Exception Safety
Objects which are created by the API are exception-safe and do not
need to be deallocated or fixed up when handling exceptions. If
pointers to API objects are returned (for example, by
ln::client::subscribe()), these are always non-owning
instances.
Objects such as port objects should never be used
longer when the life time of the corresponding
ln::client object or the objects which
were used to create them ends.
7.3.2.4. fork() calls
The fork() system call is not supported by the LN library.
This means that fork() should not be called when any LN
client instance is active. Otherwise, strange things can happen
(for example, the LN manager might become confused about
the identity of clients and their state).
7.3.2.5. Main API functions
7.3.2.5.1. Placeholders used in this description
For C++, a number of API functions are auto-generated by the
ln_generate command, so that they have names generated from
message definitions. Here, these names are placed in meta-variable
brackets, like so: <NAME>.
See also
To understand their meaning better, it can help to compare the descriptions provided here to the symbols used in C++ Tutorial. The correspondence of the placeholders used here, and the symbols there is as follows:
Placeholder in the Reference below |
Name in Tutorial Example |
|---|---|
TOPIC_PREFIX |
:= “elevator03” |
TOPIC_SUFFIX |
:= one of “sensors”, “actors”, or “prompt” |
MESSAGE_DEF_NAMESPACE |
:= one of “elevator/sensors”, “elevator/actors”, or “elevator/request” |
MDEF_NAME |
:= one of “floor_count”, “motor_control”, or “elevator_call” |
SVMDEF_NAME |
:= “elevator_call” |
CLIENT_CLASS |
:= “ElevatorClient” |
PROVIDER_CLASS |
:= “ElevatorServer” |
It is important to realize that the names of message definitions and the names of topics or services are completely orthogonal - they can be the same, but they do not have to be the same. This is because they serve different purposes: message definitions describe a (possibly general) type of data, and the topics or services describe data streams or command connections within a specific system.
7.3.2.5.2. Client class
Constructor
-
class ln::client
-
client(std::string client_name)
-
client(std::string client_name, int argc, char *argv[])
-
client(std::string client_name, std::string ln_manager)
-
client(ln::client clnt)
An LN client instance which holds information on the connection. Tries to TCP-connect to the ln-manager on construction.
client_name should be a string to help identify this client within the ln-manager. This suggestion will be ignored when the
LN_PROGRAM_NAMEenvironment variable var is set, which will take precedence and will be used used instead in this case. Attention: The suggested name is only used if no other client instance with that name is already running. The exact behavior is described in Client Name Selectionif argc and argv are set, this are assumed to be command line parameters passed from
main(), which will contain instance parameters for contacting the LN manager.if ln_manager is a string, it is set in the form
HOST:PORTof the ln-managers TCP-address.Additionally, the client will read the ln-manager address from the
LN_MANAGERenvironment variable. This will take precedence over any settings via the constructor parameters.The constructor can throw an exception if type exception with an error message in the case of run-time errors (such as a wrong address or a failure to create a connection) or an unimplemented configuration.
(For details on errors, please see
ln_clientin the C API reference.)See also
ln_clientin the C APIln_init()in the C APIln_init_to_manager()in the C APIln_clientin the C APIclientin the Python API
-
client(std::string client_name)
7.3.2.5.2.1. Client Instance Methods
-
ln::outport *ln::client::publish(std::string topic_name, std::string message_def_name, unsigned int buffers = 1)
Create an
outportfor a specific topic and message definition.- topic_name Is the name of the topic for which the data is
published. The name can be grouped into name spaces, which are separated with a dot (“.”). The topic name must not end with a leading slash or dot. For a more detailed specification, see Rules for Topic Names.
- message_definition_name The name if the message definition to use (see
the tutorial introduction on message-passing for more information). This is a full path name for the file with the message definition, separated by forward slashes (“/”). The same separator is used on non-Unix systems. The message definition is searched along the search path defined in
LN_MESSAGE_DEFINITION_DIRS.Tip
See the tutorial for more information on message definitions. A short summary in Message Definitions in the Python Tutorial chapter. For more detailed information, see ref:guide/message_definitions in the user guide. For detailed information on setting the message definition search path, see section
LN_MESSAGE_DEFINITION_DIRSin the reference part.
buffers is the number of buffers to use. The default value is 1. If data is lost, set it to a higher value.
Warning
The resources of the outport handle are owned and handled by the client instance. The life time of the handle is limited to the life time of the client instance; it cannot be used longer when the client instance get destroyed.
See also
client::publish()in the C++ APIln_publish()(C API)ln_unpublish()(C API)
-
ln::inport *ln::client::subscribe(std::string topic_name, std::string message_def_name, double rate = -1, unsigned int buffers = 1, bool need_reliable_transport = false)
Subscribe to named topic and returns an instance of
inport.- topic_name is the name of the topic.
The name can be grouped into name spaces, which are separated with a dot (“.”). The topic name must not end with a leading slash or dot. For a more detailed specification, see Rules for Topic Names.
- message_definition_name is the name of the message definition
to use, as a path name from the message-definition folder to the message definition file, separated by slashes (”"). If message_definition_name is
Noneand the topic is already published, the message definition name will be retrieved from the ln-manager.- rate is a limit which specifies the maximum sending-rate in
messages per second. With the default of
-1the topic will be subscribed without rate-limiter, the publisher decides the update-rate. Note that messages exceeding the rate limit will not be seen by the subscriber, even if they were sent correctly.Proper rate-limiting messages requires that the messages which are sent by publishers have the timestamp parameter of the
ln::port::write()set in the unit of seconds.
- buffers is the number of message buffers to use. Increasing the
number of buffers can prevent problems if a receiver is temporarily delayed, but messages should not be lost. If no empty buffers are available, previous messages are overwritten by new messages (CHECK). The default value is 1; a good value is often 3. If data is lost, set this number to a higher value, or make sure that the receiving process works with minimum latency as a real-time process.
- need_reliable_transport is of interest when the topic needs to be
transported across a network. If set to
trueTCP will be used to get a reliable communication channel. Otherwise UDP is used.
Warning
The resources of the inport handle are owned and handled by the client instance. The life time of the handle is limited to the life time of the client instance; it cannot be used longer when the client instance get destroyed.
Tip
See the tutorial for more information on message definitions. A short summary in Message Definitions in the C++ Tutorial chapter. For more detailed information, see ref:guide/message_definitions in the user guide. For detailed information on setting the message definition search path, see section
LN_MESSAGE_DEFINITION_DIRSin the reference part.Note
If the message_definition_name is specified but is unknown to the ln-manager, an
Exceptionexception is raised.See also
ln_subscribe()(C API)
-
ln::client::wait_and_handle_service_group_requests(const char *group_name, double timeout = -1)
Handles all pending service requests for a service group. The handlers will all run in the current thread (usually, the main thread), and will not be executed concurrently. This method should be used by default, as it is much safer and simpler than concurrent handling of requests, which is difficult to do correctly.
Calling this method is necessary to serve requests.
Note
Concurrent calls of
wait_and_handle_service_group_requests()for the same service group are serialized internally. This is a safety guard, not a recommended scheduling model. Prefer one dispatcher thread per service group, or useln::client::handle_service_group_in_thread_pool()when service handlers should run concurrently.group_name name of the service group. The parameter can be set to nullptr to use a default value.
timeout The maximum time, in seconds, that the function will wait for requests before returning. This can be used to handle requests to different service groups in quick succession, by handling them sequentially, while not incurring risk that bugs occur because of incorrect locking.
-
ln::client::handle_service_group_in_thread_pool(const char *group_name, std::string pool_name)
Defines the name of a service group and a thread pool for service threads, and starts to handle service requests.
This method is an alternative to
ln::client::wait_and_handle_service_group_requests(). In difference to the latter, service requests are handled concurrently, which can yield lower latencies or be faster. Important: Any data that is modified during a service request (by the handling thread or by another thread) needs to be protected with mutexes. If in doubt, use alwaysln::client::wait_and_handle_service_group_requests(), because correct programming in multiple threads is much more difficult and prone to non-deterministic bugs such as race conditions, which can cause undefined behavior.group_name name of the service group. This value can be set to
nullptr.pool_name name of the thread pool. This name can be chosen freely, for example it can be set to “main pool”. If a pool with that name does not exist, it will be automatically created with a pool size of 1. The number of threads can be set by using the
ln::client::set_max_threads()method.
See also
ln_handle_service_group_in_thread_pool()in the C API
-
ln::client::set_max_threads(std::string pool_name, unsigned int n_threads)
Sets the maximum number of threads for a thread pool with the given name. Here, the number
n_threadsmeans:Only one service request can be handled at one time, the requests for the corresponding service groups are handled sequentially.
Still, only one service request is handled at the same time, but the setup of the connection is done in an own thread.
Up to two service requests can be handled at the same time (and so on).
Warning
Remember that if different service handlers (or any other parts of the same program) access and modify the same data at the same time, it needs to be protected by mutexes or locks, in order to avoid data corruption. Otherwise, non-deterministic bugs such as race conditions can occur.
See also
ln_set_max_threads()in the C API
-
class ln::service
An object representing a connection to request and handle service calls.
Services are accessed by handles. There are two kinds of handles, a service client handle, and a service provider handle, which are created using different functions.
Services are functionally similar to ports, in that they describe an open connection, with the difference that they represent a two-way connection which allows to perform remote procedure calls.
Class instances can be created by calling
client::get_service(), after an LNclientinstance has been registered.serviceinstances behave similarly toportobjects, with the difference that they are designed to be used for a kind of remote procedure call, while ports can only be used to write or read messages unidirectionally.See also
ln_service(C API)ln_service_init(C API)ln_service_deinit(C API)
-
ln::service *ln::client::get_service(std::string service_name, std::string message_definition_name, std::string signature)
Get a pointer (handle) to a service client instance.
- service_name is a string with the name of the
service. As with topic names, it can have name space separators in the form of dots (“.”). It must not contain slashes (neither within, or at the end).
- message_definition_name (also named
service_interface in some headers, but without any different meaning) is a string with the name of the message definition for the service.
- signature is a string which represents the layout
of the service message data buffer for a specific service message definition. This signature string is auto-generated by ln_generate, which defines a constant in the header
ln_messages.h, that needs to be included by the C++ program.
Warning
The resources of the service handle are owned and handled by the client instance. The life time of the handle is limited to the life time of the client instance; it cannot be used longer when the client instance get destroyed.
-
ln::service *ln::client::get_service_provider(std::string service_name, std::string service_interface, std::string signature)
Get a pointer (handle) to a service provider instance.
Important
Note that this handle is not usable for a service client!
- service_name is a string with the name of the
service. As with topic names, it can have name space separators in the form of dots (“.”). It must not contain slashes (neither within, or at the end).
- service_interface is a string with the name of the
message definition for the service.
- signature is a string which represents the layout
of the service message data buffer for a specific service message definition. This signature string is auto-generated by ln_generate, which defines a constant in the header
ln_messages.h, that needs to be included by the C++ program.
Warning
The resources of the service handle are owned and handled by the
ln::clientinstance. The life time of the handle is limited to the life time of the client instance; it cannot be used longer when the client instance get destroyed.See also
7.3.2.5.3. Port Base Class
-
class port_base
Is a connection of a subscriber or publisher to send and receive messages of a given type. Ports have two sub-types, one for sending and one for receiving data,
7.3.2.5.4. Output Ports
-
class outport
Handle to connection and buffer for sending messages
See also
ln_outport(C API)
7.3.2.5.4.1. Output port Methods
-
void ln::outport::write(void *data)
-
void ln::outport::write(void *data, double timestamp)
Write and transmit request a packet of message data of length :cpp:member`port_base::message_size`. The data is always transmitted without blocking and completely. Both the connection and the required buffer space are internal parameters of the port instance.
- (void) data* pointer to the start of the data that is
being transmitted. The size of the data blob is derived from the message definition that was given at the port’s creation.
timestamp: is a floating-point number which can be used as a time-stamp. The time-stamp can indicate whether data is current, and is also used for rate-limiting, if a subscriber has a rate-limit set. The unit of the timestamp needs to be in seconds.
The function can throw an exception if type exception in the case of internal errors, which will contain a diagnostic message.
TODO: Which exceptions can be thrown, what do they mean, and how are these addressed?
See also
ln_write()(C API)
7.3.2.5.5. Input Ports
-
class inport
An
inportis a connection and buffer space for receiving data via theln::inport::read()function.See also
ln_inport(C API)
7.3.2.5.5.1. Input Port Methods
-
bool ln::inport::read(void *data, bool blocking = true)
-
bool ln::inport::read(void *data, double timeout)
The port
read()method copies an available packet of message data of size :cpp:member`port_base::message_size` after it was been received by the port. Depending on blocking and time-out parameters, it will wait or will not wait when no new data is currently present.blocking_or_timeout is a parameter which controls blocking and time-out behavior:
If it is Boolean
true(which is the default value) and no new data is present, theread()call will block until new data is available.If it is a floating point number and no data is available yet, it will wait at most this fractional number of seconds before it returns
falseand without transferring data.In this case, the content of the buffer pointed to by
datais not changed.If it is a number of zero, a negative number, or a Boolean with the value of
false, the port will be non-blocking: If no new data is present, it will never wait, but immediately return a Booleanfalse.(For further explanation, see the description in the tutorial.)
in any case, the data is either copied completely or not at all, and if no new data was send, the input buffer will not be changed, keeping its old content.
return value is a Boolean value of
trueif data could be read, and otherwise BooleanFalse, meaning that either no data was present, or a time-out was set and the time-out expired without receiving data.The function can throw an exception if type exception in the case of errors or an unimplemented configuration, which will contain a diagnostic message. For details, please see the C APO reference.
See also
ln_read()(C API)ln_read_timeout()(C API)for an overview on how to use publish/subscribe communication with ports and topics, see API for Topics communication with publish / subscribe in the tutorial.
7.3.2.5.6. Service calls (client side)
-
type MESSAGE_DEF_NAMESPACE::SVMDEF_NAME_t
Message buffer for service data, which has a
reqmember for request input parameters, and arespfor the return parameters.Important
Both at the client side, and the provider side, the user-accessed buffers need to be allocated by the user program (for example, as an automatic variable on the stack). Apart from the service wrapper class, the LN system does not allocate user-accessible memory space for these data.
Conseqently, the service request parameters need to be default-initialized by the user, in order to avoid uninitialized memory.
7.3.2.5.6.1. Methods
-
ln::service::call(MESSAGE_DEF_NAMESPACE::MDEF_NAME_t *request_parameters)
Calls an LN service by sending the request parameter struct with the parameters stored in
MESSAGE_DEF_NAMESPACE::MDEF_NAME_t::reqto the service provider, waiting for a response, and making the result parameters available in theMESSAGE_DEF_NAMESPACE::MDEF_NAME_t::respdata attribute.The user needs to take care that the
MESSAGE_DEF_NAMESPACE::MDEF_NAME_t::reqis properly initialized for all fields that are not specifically assigned to, for example using the Cmemset()function, or an C++ “{}” default initializer.The used data structure with the generated type name MESSAGE_DEF_NAMESPACE::MDEF_NAME_t is not type-checked at compile time. However, it is designed to be a structure definition that is generated by ln_generate, which allows to access the data elements safely (see section Service data buffer).
See also
ln_service_call()(C API)ln_service_handler(C API)ln_service_init(C API)ln_service_deinit(C API)ln_service_provider_register()(C API)ln_service_request_respond()(C API)
See also
ln_service_call()(C API)
-
typedef int (*handler_t)(ln::client&, service_request&, void *user_data)
User-provided callback function that handles a service request. (In the wrapped API, this function is pre-defined by the wrapper class).
See also
ln_service_handlerin the C API
-
void ln::service::set_handler(handler_t handler, void *user_data = NULL)
Sets a user-provided handler of type
handler_tfor a service call which needs to have the following signature:int USER_SERVICE_HANDLER(ln::client& clnt, ln::service_request& req, void* user_data)
See also
-
int USER_SERVICE_HANDLER(ln::client &clnt, ln::service_request &req, void *user_data)
The handler is a function of type
handler_tfor a user-provided function which accepts a reference to anln::clientinstance as a first parameter, a reference to aln::service_requestas the second parameter, and an optional pointer to some user data as third parameter.- user_data The user data can be some pointer to a
class instance or a static variable which provides context to the service call, or it can be used to store state between calls.
Important
This data pointer is not to be confused with message data for a single service request. It is intended to hold memory or a pointer to an object that has the task to respond to these requests, for example a kind of hardware driver or data cache.
req The req parameter of the handler function prototype is an instance to an
ln::service_requestobject which can retrieve and copy the call parameters using theln::service_request::set_data()function, and send the response using theln::service_request::respond()function.clnt The
ln::clientreference is not necessarily used.
-
void ln::service::do_register(const char *group_name = nullptr)
Registers a service represented by a service provider handle to be handled in a specific thread group.
In difference to
ln::client::get_service_provider()andln::service::set_handler(), this function performs communication with the LN manager. Because network connections are not absolutely reliable, this operation can potentially fail, which will result in an exception.group_name The name of the service group. If the parameter is
NULLornullptr, the default service group is used.See also
ln_service_provider_register()(C API)
-
class ln::service_request
Is a handle to an object which represents a service request, and provides methods to finishing and returning, or aborting the request.
Its two primary capabilities are to copy the request data into a user-space buffer (using the
ln::service_request::set_data()method), and to return the response data, using theln::service_request::respond()method.It is also possible to call the service request asynchronously, which is useful for real-time applications in which blocking calls are undesirable.
This object does not contain the request and response data — both are accessed via an separate data structure called MESSAGE_DEF_NAMESPACE::MDEF_NAME_t, which is auto-generated by ln_generate (see section Service data buffer).
See also
ln_service_request(C API)
-
int ln::service_request::set_data(MESSAGE_DEF_NAMESPACE::SVMDEF_NAME_t *data, char *signature)
Copies the transmitted data from the internal LN message buffer for a service message into a user-provided buffer pointed to by data.
data User-space buffer for the call data. The buffer needs to hold both the request parameters, as well as the response which will be later filled in by the client handler. For the type of this parameter, see section Symbols generated by ln_generate.
signature is a string which holds a description of the corresponding message definition, which is auto-generated and has the name MESSAGE_DEF_NAMESPACE::SVMDEF_NAME_signature. This string constant is generated by ln_generate. The string has the function to define how much data has to be copied.
See also
ln_service_request_set_data()(C API)
-
int ln::service_request::respond()
This method needs to be called when a service request has been processed by a service provider process, and the request is returned as successful with the resulting response data and resolved.
The result is that the result data in the associated data buffer of type MESSAGE_DEF_NAMESPACE::MDEF_NAME_t is copied back from the user buffer assigned by set-data into the internal LN message buffers, and transmitted to the service client.
Any error while transferring the response will raise an exception here. When
respond()returns, the provider can be sure that the response has arrived at the caller, and that both processes have reached the corresponding point of their execution (in other words, therespond()method provides synchronization between threads and between service clients and the service provider).Note
Depending on the used service message definition, the definition can contain variable-length arrays, and in this case, the response can contain pointers to dynamically allocated objects (such as strings) which have a limited life time. In such cases, it is important that ``respond()`` is called before the life time of these objects expires. Otherwise, corrupted data might be returned.
See also
ln_service_request_respond()(C API)
-
bool ln::service_request::is_aborted()
Method which allows to check whether a service request was aborted.
-
bool ln::service_request::finished()
Method indicating that a service request has been finished. This is used for asynchronous calls.
-
void ln::service_request::abort()
Method that aborts a service request. This can be used in asynchronous calls.
7.3.2.5.7. Symbols generated by ln_generate
See the section “placeholders used in this description” above for explanations on the meaning of the placeholders in angle brackets, and how they correspond to the tutorial code examples.
7.3.2.5.7.1. Structs generated from Message Definitions (for both topics and services)
7.3.2.5.7.2. Service data buffer
-
class MESSAGE_DEF_NAMESPACE::MDEF_NAME_t
Type with which holds the data for sending the request, and allows to retrieve the result. Is an auto-generated struct type. It contains both the request parameters and the response data for a service call.
The service client needs to fill in the request parameter data, before calling the service, and gets the response data in return. Request parameters and response data are defined via two members of of this auto-generated structure type.
-
SERVICE_PREFIX_SERVICE_SUFFIX_MDEF_NAME_request_t MESSAGE_DEF_NAMESPACE::SVMDEF_NAME::req
Is a data member object which receives the service request or input parameters from the client.
See also
ln_service_request(C API)
-
SERVICE_PREFIX_SERVICE_SUFFIX_MDEF_NAME_response_t MESSAGE_DEF_NAMESPACE::SVMDEF_NAME::resp
is a data member which returns the service return parameters to the client.
Its content is copied when the
ln::service_request::respond()function is called.The content of the response buffer needs to be filled in by the called service provider. The data buffer is initialized to zero (using
memset(void *data, 0, ...)) before it is handed to the provider function.Note
The response object can contain arrays which have a variable number of elements (so-called variable-length arrays). These are represented by pointers to another memory object, typically a dynamically allocated and managed object or buffer space like std::string::c_str(). It is important that the life time of these objects is at least as long as to the point where the call to
ln::service_request::respond()takes place.
7.3.2.5.7.3. Classes and Methods auto-generated for service providers
-
class MESSAGE_DEF_NAMESPACE::SVMDEF_NAME_base
Base class provided by
ln_generate, that is used to derive the provider class, which is defined by the developer using LN.
-
MESSAGE_DEF_NAMESPACE::SVMDEF_NAME_base::register_SVMDEF_NAME(ln::client, char *service_fullname)
Method of the service provide base class which registers the service method, with the name of the service (for example, “elevator.request”) as a parameter.
-
PROVIDER_CLASS::on_SVMDEF_NAME(ln::service_request &req, MESSAGE_DEF_NAMESPACE::MDEF_NAME_t &svc) override
Method of the provider class, that is implemented by it, and is run when the service is called, with the service message as a parameter. The method takes the parameter svc, reads the call parameters from
svc.req, and when it is ready, it stores the result of the call insvc.resp, and callsreq.respond(), which is defined as member functionln::service_request::respond().
-
PROVIDER_CLASS::run()
Method which runs the service provider, and needs to be implemented by the provider class.
7.3.2.5.7.4. Symbols generated for service client
The client class does not need to be derived by a specific base class.
It instantiates an object of the type :cpp:class::ln::service(),
using client::get_service().
-
CLIENT_CLASS::SVMDEF_NAME()
Client function that calls the service, via
ln::service::call().The name of this function is conventional, it is not auto-generated.
See also
For an overview on how to use service calls, the the tutorial section API for using LN Services from C++
7.3.2.6. Relevant Source Code Files for further Reference
7.3.2.6.1. C++ API
python/links_and_nodes/libln/include/ln/cppwrapper.h
python/links_and_nodes/_ln.cpp
7.3.2.6.2. C API
python/links_and_nodes/ln_wrappers.py
libln/include/ln/cpp_wrapper.h
libln/include/ln/cpp_wrapper_impl.h
libln/include/ln/ln.h