10.8. Quickstart: Handling Services with auto-generated Wrapper Code from C++

This addition to the quickstart examples shows how to define a service provider in C++ by usinng auto-generated wrapper classes.

It is intended for cases where a lot of code would need to be generated.

Warning

Generally, the example given in Calling and handling Services from C++ is preferable and recommended to use, because the resulting code and API is easier to explain and understand than code based on auto-generated code, even if it is a bit longer.

10.8.1. Defining a “wrapped” service provider in C++

Here is how the “wrapped” service provider class looks:

  1#include <ln/ln.h>
  2#include <cstdint>
  3#include "area_api_constants.h"
  4#include <iostream>
  5#include <cmath>
  6
  7#include "ln_messages.h"
  8
  9
 10using std::cout;
 11
 12
 13
 14class AreaServer :
 15	public area_service::circle_area_base,
 16	public area_service::ellipse_area_base
 17{
 18
 19	ln::client clnt;
 20public:
 21	AreaServer() :
 22		clnt("C++ area service")
 23	{
 24
 25		register_circle_area(&clnt, "area_service.circle_area");
 26		register_ellipse_area(&clnt, "area_service.ellipse_area");
 27
 28	}
 29
 30
 31	int on_circle_area(ln::service_request& req,
 32	                   area_service::circle_area_t& data) override
 33	{
 34
 35
 36		if (data.req.radius == 0.0) {
 37			data.resp.error_code = static_cast<uint32_t>(E_AreaErrorStatus::ZERO_INPUT);
 38			data.resp.error_message = (char*)"on_circle_area(): input was zero";
 39
 40		} else if (data.req.radius < 0.0) {
 41			data.resp.area = NAN;
 42			data.resp.error_code = static_cast<uint32_t>(E_AreaErrorStatus::NEGATIVE_INPUT);
 43			data.resp.error_message = (char*)"on_circle_area(): input was negative";
 44
 45		} else {
 46			const double radius = data.req.radius;
 47			data.resp.area = M_PI * radius * radius;
 48
 49			data.resp.error_code = static_cast<uint32_t>(E_AreaErrorStatus::NO_ERROR);
 50			data.resp.error_message = (char*)"";
 51		}
 52		data.resp.error_message_len = strlen(data.resp.error_message);
 53		req.respond();
 54
 55
 56		cout << "request finished!\n";
 57		return 0;
 58	}
 59
 60	int on_ellipse_area(ln::service_request& req,
 61	                    area_service::ellipse_area_t& data) override
 62	{
 63
 64
 65		if ((data.req.a < 0.0) || (data.req.b < 0.0)) {
 66
 67			data.resp.area = NAN;
 68			data.resp.error_code = static_cast<uint32_t>(E_AreaErrorStatus::NEGATIVE_INPUT);
 69			data.resp.error_message = (char*)"on_ellipse_area(): input was negative";
 70
 71		} else if ((data.req.a == 0.0) || (data.req.b == 0.0)) {
 72
 73			data.resp.error_code = static_cast<uint32_t>(E_AreaErrorStatus::ZERO_INPUT);
 74			data.resp.error_message = (char*)"on_ellipse_area(): input was zero";
 75
 76		} else  {
 77			const double a = data.req.a;
 78			const double b = data.req.a;
 79			data.resp.area = M_PI * a * b;
 80
 81			data.resp.error_code = static_cast<uint32_t>(E_AreaErrorStatus::NO_ERROR);
 82			data.resp.error_message = (char*)"";
 83		}
 84		data.resp.error_message_len = strlen(data.resp.error_message);
 85		req.respond();
 86
 87
 88		cout << "request finished!\n";
 89		return 0;
 90	}
 91
 92
 93	int run()
 94	{
 95		cout << "ready to receive service calls\n";
 96		while (1) {
 97			/// processing...
 98			double time_out = -1; // means blocking
 99			clnt.wait_and_handle_service_group_requests(NULL, time_out);
100		}
101	}
102
103
104};
105
106
107int main(int argc, char* argv[])
108{
109	AreaServer area_server;
110	return area_server.run();
111}

10.8.1.1. Initialization

We start with the initialization of the provider class. The class is derived from two auto-generated classes named area_service::circle_area_base, and area_service::ellipse_area_base. These were auto-generated by ln_generate, and included in ln_messages.

Lines 25 and 26 is the code which initializes the service provider class and its handlers for service message requests. Here, the service handler for the message definition area_service/circle_area is initialized with the name of the service, and the same happens with the service for area_service/ellipse_area.

The names of the handler methods are hard-coded and auto-generated from the message definitions. For the message name area_service/circle_area, it is on_circle_area(), and for area_service/ellipse_area, on_ellipse_area().

10.8.1.2. Handler Methods

The handler method on_circle_area() is shown in lines 31 to 58.

10.8.1.2.1. The Request Data Buffer

For each method, a buffer with the name data is passed, which has two members, req and resp. This buffer has the purpose to hold the request parameter data, as well as the response data. Its type is of type area_service::circle_area_t, which was auto-generated by ln_generate from the message definition with the name “area_service/circle_area” - each slash in the message definition name is converted into a nested C++ namespace identifier.

The type name area_service::circle_area_t is formed from the message definition that we use, "area_service/circle_area", by replacing each slach (“/”) with a double-colon (“::”), and appending an “_t”. This is a type that is auto-generated by ln_generate from the message definitions when make builds the header file ln_messages as an intermediate target. This can also be described as data.req having the auto-generated type MESSAGE_DEF_NAMESPACE::SVMDEF_NAME::req. Here, MESSAGE_DEF_NAMESPACE::SVMDEF_NAME is the meta-typename constructed from the name of the message definition with each slash (“/”) replaced by a pair of colons (“::”).

After that, the service handler can access the request parameters to the member fields of data.req. In our case, this is data.req.radius.

The response data has to be places into the member fields of data.resp, which is of type MESSAGE_DEF_NAMESPACE::SVMDEF_NAME::resp.

The method AreaServer::on_ellipse_area() works in the same way.

10.8.1.3. Running the Service Provider

The remaining parts of the service provider class work as described in Calling and handling Services from C++.

Tip

A more detailed example for the “wrapped” API can be found in the chapter Using Service Wrappers from C++.