4 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
5 .. SPDX-License-Identifier: CC-BY-4.0
7 .. CAUTION: this document is generated from source in doc/src/*
8 .. To make changes edit the source and recompile the document.
9 .. Do NOT make changes directly to .rst or .md files.
17 The C++ framework allows the programmer to create an xApp
18 object instance, and to use that instance as the logic base.
19 The xApp object provides a message level interface to the RIC
20 Message Router (RMR), including the ability to register
21 callback functions which the instance will drive as messages
22 are received; much in the same way that an X-windows
23 application is driven by the window manager for all activity.
24 The xApp may also choose to use its own send/receive loop,
25 and thus is not required to use the callback driver mechanism
26 provided by the framework.
32 The C++ framework API consists of the creation of the xApp
33 object, and invoking desired functions via the instance of
34 the object. The following paragraphs cover the various steps
35 involved to create an xApp instance, wait for a route table
36 to arrive, send a message, and wait for messages to arrive.
39 Creating the xApp instance
40 --------------------------
42 The creation of the xApp instance is as simple as invoking
43 the object's constructor with two required parameters:
53 A C string (pointer to char) which defines the port that
54 RMR will open to listen for connections.
61 A Boolean value which indicates whether or not the
62 initialization process should wait for the arrival of a
63 valid route table before completing. When true is
64 supplied, the initialization will not complete until RMR
65 has received a valid route table (or one is located via
66 the ``RMR_SEED_RT`` environment variable).
70 The following code sample illustrates the simplicity of
71 creating the instance of the xApp object.
77 #include <ricxfcpp/xapp.hpp>
79 std::unique_ptr<Xapp> xapp;
80 char* listen_port = (char *) "4560"; //RMR listen port
81 bool wait4table = true; // wait for a route table
83 xapp = std::unique_ptr<Xapp>(
84 new Xapp( listen_port, wait4table ) );
87 Figure 1: Creating an xAPP instance.
89 From a compilation perspective, the following is the simple
90 compiler invocation string needed to compile and link the
91 above program (assuming that the sample code exists in a file
92 called ``man_ex1.cpp``.
97 g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
100 The above program, while complete and capable of being
101 compiled, does nothing useful. When invoked, RMR will be
102 initialized and will begin listening for a route table;
103 blocking the return to the main program until one is
104 received. When a valid route table arrives, initialization
105 will complete and the program will exit as there is no code
106 following the instruction to create the object.
109 LISTENING FOR MESSAGES
110 ======================
112 The program in the previous example can be extended with just
113 a few lines of code to enable it to receive and process
114 messages. The application needs to register a callback
115 function for each message type which it desires to process.
117 Once registered, each time a message is received the
118 registered callback for the message type will be invoked by
125 As with most callback related systems, a callback must have a
126 well known function signature which generally passes event
127 related information and a "user" data pointer which was
128 registered with the function. The following is the prototype
129 which callback functions must be defined with:
134 void cb_name( Message& m, int mtype, int subid,
135 int payload_len, Msg_component payload,
138 Figure 2: Callback function signature
140 The parameters passed to the callback function are as
151 A reference to the Message that was received.
158 The message type (allows for disambiguation if the
159 callback is registered for multiple message types).
166 The subscription ID from the message.
173 The number of bytes which the sender has placed into the
181 A direct reference (smart pointer) to the payload. (The
182 smart pointer is wrapped in a special class in order to
183 provide a custom destruction function without burdening
184 the xApp developer with that knowledge.)
191 A pointer to user data. This is the pointer that was
192 provided when the function was registered.
196 To illustrate the use of a callback function, the previous
197 code example has been extended to add the function, register
198 it for message types 1000 and 1001, and to invoke the
199 ``Run()`` function in the framework (explained in the next
205 #include <ricxfcpp/xapp.hpp>
206 long m1000_count = 0; // message counters, one for each type
207 long m1001_count = 0;
209 // callback function that will increase the appropriate counter
210 void cbf( Message& mbuf, int mtype, int subid, int len,
211 Msg_component payload, void* data ) {
214 if( (counter = (long *) data) != NULL ) {
220 std::unique_ptr<Xapp> xapp;
221 char* listen_port = (char *) "4560";
222 bool wait4table = false;
224 xapp = std::unique_ptr<Xapp>(
225 new Xapp( listen_port, wait4table ) );
227 // register the same callback function for both msg types
228 xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
229 xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
231 xapp->Run( 1 ); // start the callback driver
234 Figure 3: Callback function example.
236 As before, the program does nothing useful, but now it will
237 execute and receive messages. For this example, the same
238 function can be used to increment the appropriate counter
239 simply by providing a pointer to the counter as the user data
240 when the callback function is registered. In addition, a
241 subtle change from the previous example has been to set the
242 wait for table flag to ``false.``
244 For an xApp that is a receive only application (never sends)
245 it is not necessary to wait for RMR to receive a table from
249 Registering A Default Callback
250 ------------------------------
252 The xApp may also register a default callback function such
253 that the function will be invoked for any message that does
254 not have a registered callback. If the xAPP does not register
255 a default callback, any message which cannot be mapped to a
256 known callback function is silently dropped. A default
257 callback is registered by providing a *generic* message type
258 of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
261 The Framework Callback Driver
262 -----------------------------
264 The ``Run()`` function within the Xapp object is invoked to
265 start the callback driver, and the xApp should not expect the
266 function to return under most circumstances. The only
267 parameter that the ``Run()`` function expects is the number
268 of threads to start. For each thread requested, the framework
269 will start a listener thread which will allow received
270 messages to be processed in parallel. If supplying a value
271 greater than one, the xApp must ensure that the callback
272 functions are thread safe as it is very likely that the same
273 callback function will be invoked concurrently from multiple
280 It is very likely that most xApps will need to send messages
281 and will not operate in "receive only" mode. Sending the
282 message is a function of the message object itself and can
283 take one of two forms:
286 * Replying to the sender of a received message
288 * Sending a message (routed based on the message type and
292 When replying to the sender, the message type and
293 subscription ID are not used to determine the destination of
294 the message; RMR ensures that the message is sent back to the
295 originating xApp. The xApp may still need to change the
296 message type and/or the subscription ID in the message prior
297 to using the reply function.
299 To provide for both situations, two reply functions are
300 supported by the Message object as illustrated with the
301 following prototypes.
306 bool Send_response( int mtype, int subid, int response_len,
307 std:shared_ptr<unsigned char> response );
309 bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
311 Figure 4: Reply function prototypes.
313 In the first prototype the xApp must supply the new message
314 type and subscription ID values, where the second function
315 uses the values which are currently set in the message.
316 Further, the new payload contents, and length, are supplied
317 to both functions; the framework ensures that the message is
318 large enough to accommodate the payload, reallocating it if
319 necessary, and copies the response into the message payload
320 prior to sending. Should the xApp need to change either the
321 message type, or the subscription ID, but not both, the
322 ``NO_CHANGE`` constant can be used as illustrated below.
327 msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
328 pl_length, (unsigned char *) payload );
330 Figure 5: Send response prototype.
332 In addition to the two function prototypes for
333 ``Send_response()`` there are two additional prototypes which
334 allow the new payload to be supplied as a shared smart
335 pointer. The other parameters to these functions are
336 identical to those illustrated above, and thus are not
339 The ``Send_msg()`` set of functions supported by the Message
340 object are identical to the ``Send_response()`` functions and
346 bool Send_msg( int mtype, int subid, int payload_len,
347 std::shared_ptr<unsigned char> payload );
349 bool Send_msg( int mtype, int subid, int payload_len,
350 unsigned char* payload );
352 bool Send_msg( int payload_len,
353 std::shared_ptr<unsigned char> payload );
355 bool Send_msg( int payload_len, unsigned char* payload );
357 Figure 6: Send function prototypes.
359 Each send function accepts the message, copies in the payload
360 provided, sets the message type and subscription ID (if
361 provided), and then causes the message to be sent. The only
362 difference between the ``Send_msg()`` and
363 ``Send_response()`` functions is that the destination of the
364 message is selected based on the mapping of the message type
365 and subscription ID using the current routing table known to
369 Direct Payload Manipulation
370 ---------------------------
372 For some applications, it might be more efficient to
373 manipulate the payload portion of an Xapp Message in place,
374 rather than creating it and relying on a buffer copy when the
375 message is finally sent. To achieve this, the xApp must
376 either use the smart pointer to the payload passed to the
377 callback function, or retrieve one from the message using
378 ``Get_payload()`` when working with a message outside of a
379 callback function. Once the smart pointer is obtained, the
380 pointer's get() function can be used to directly reference
381 the payload (unsigned char) bytes.
383 When working directly with the payload, the xApp must take
384 care not to write more than the actual payload size which can
385 be extracted from the Message object using the
386 ``Get_available_size()`` function.
388 When sending a message where the payload has been directly
389 altered, and no extra buffer copy is needed, a NULL pointer
390 should be passed to the Message send function. The following
391 illustrates how the payload can be directly manipulated and
392 returned to the sender (for simplicity, there is no error
393 handling if the payload size of the received message isn't
394 large enough for the response string, the response is just
400 Msg_component payload; // smart reference
401 int pl_size; // max size of payload
403 payload = msg->Get_payload();
404 pl_size = msg->Get_available_size();
405 if( snprintf( (char *) payload.get(), pl_size,
406 "Msg Received\\n" ) < pl_size ) {
407 msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
410 Figure 7: Send message without buffer copy.
414 Sending Multiple Responses
415 --------------------------
417 It is likely that the xApp will wish to send multiple
418 responses back to the process that sent a message that
419 triggered the callback. The callback function may invoke the
420 ``Send_response()`` function multiple times before returning.
422 After each call, the Message retains the necessary
423 information to allow for a subsequent invocation to send more
424 data. It should be noted though, that after the first call to
425 ``{Send_response()`` the original payload will be lost; if
426 necessary, the xApp must make a copy of the payload before
427 the first response call is made.
433 Not all xApps will be "responders," meaning that some xApps
434 will need to send one or more messages before they can expect
435 to receive any messages back. To accomplish this, the xApp
436 must first allocate a message buffer, optionally initialising
437 the payload, and then using the message's ``Send_msg()``
438 function to send a message out. The framework's
439 ``Alloc_msg()`` function can be used to create a Message
440 object with a desired payload size.
443 FRAMEWORK PROVIDED CALLBACKS
444 ============================
446 The framework itself may provide message handling via the
447 driver such that the xApp might not need to implement some
448 message processing functionality. Initially, the C++
449 framework will provide a default callback function to handle
450 the RMR based health check messages. This callback function
451 will assume that if the message was received, and the
452 callback invoked, that all is well and will reply with an OK
453 state. If the xApp should need to override this simplistic
454 response, all it needs to do is to register its own callback
455 function for the health check message type.
461 The C++ xAPP framework provides a very lightweight json
462 parser and data hash facility. Briefly, a json hash (Jhash)
463 can be established by creating an instance of the Jhash
464 object with a string of valid json. The resulting object's
465 functions can then be used to read values from the resulting
469 Creating The Jhash Object
470 -------------------------
472 The Jhash object is created simply by passing a json string
477 #include <ricxfcpp/Jhash>
479 std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
482 jh = new Jhash( jstring.c_str() );
484 Figure 8: The creation of the Jhash object.
486 Once the Jhash object has been created any of the methods
487 described in the following paragraphs can be used to retrieve
494 Json objects can be nested, and the nesting is supported by
495 this representation. The approach taken by Jhash is a
496 "directory view" approach, where the "current directory," or
497 current *blob,* limits the scope of visible fields.
499 As an example, the json contained in figure jblob_fig,
500 contains a "root" blob and two *sub-blobs* (address and
506 "lodge_name": "Water Buffalo Lodge 714",
508 "grand_poobah": "Larry K. Slate",
509 "attendance": [ 23, 14, 41, 38, 24 ],
511 "street": "16801 Stonway Lane",
517 "owner": "Stonegate Properties",
520 "contact:" "Kyle Limestone"
524 Figure 9: Sample json with a root and too blobs.
526 Upon creation of the Jhash object, the *root* fields,
527 ``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
528 immediately available. The fields in the *sub-blobs* are
529 avalable only when the correct blob is selected. The code
530 sample in figure 10 illustrates how a *sub-blob* is selected.
534 jh->Set_blob( (char *) "address" ); // select address
535 jh->Unset_blob(); // return to root
536 jh->Set_blob( (char *) "lease_info" ); // slect the lease blob
538 Figure 10: Blob selection example.
540 Currently, the selected blob must be unset in order to select
541 a blob at the root level; unset always sets the root blob.
542 Attempting to use the ``Set_blob`` function will attempt to
543 select the named blob from the current blob, and not the
547 Simple Value Extraction
548 -----------------------
550 Simple values are the expected data types *string, value,*
551 and *boolean.* This lightweight json parser treats all values
552 as floating point numbers and does not attempt to maintain a
553 separate integer type. A fourth type, *null,* is supported to
554 allow the user to expressly check for a field which is
555 defined but has no value; as opposed to a field that was
556 completely missing from the data. The following are the
557 prototypes for the functions which allow values to be
563 std::string String( const char* name );
564 float Value( const char* name );
565 bool Bool( const char* name );
568 Each of these funcitons returns the value associated with the
569 field with the given *name.* If the value is missing, the
570 following default values are returned:
580 An empty string (.e.g "").
596 If the user needs to disambiguate between a missing value and
597 the default value either the ``Missing`` or ``Exists``
598 function should be used first.
601 Testing For Existing and Missing Fields
602 ---------------------------------------
604 Two functions allow the developer to determine whether or not
605 a field is included in the json. Both of these functions work
606 on the current *blob,* therefore it is important to ensure
607 that the correct blob is selected before using either of
608 these funcitons. The prototpyes for the ``Exists`` and
609 ``Missing`` functions are below:
613 bool Exists( const char* name );
614 bool Is_missing( const char* name );
616 The ``Exists`` function returns *true* if the field name
617 exists in the json and *false* otherwise. Conversly, the
618 ``Missing`` funciton returns *true* when the field name does
619 not exist in the json.
625 The ``Exists`` and ``Missing`` functions might not be enough
626 for the user code to validate the data that it has. To assist
627 with this, several functions allow direct type testing on a
628 field in the current blob. The following are the prototypes
633 bool Is_bool( const char* name );
634 bool Is_null( const char* name );
635 bool Is_string( const char* name );
636 bool Is_value( const char* name );
639 Each of these funcitons return *true* if the field with the
640 given name is of the type being tested for.
646 Arrays are supported in the same manner as simple field
647 values with the addition of the need to supply an array index
648 when fetching values from the object. In addition, there is a
649 *length* function which can be used to determine the number
650 of elements in the named array. The prototypes for the array
651 based functions are below:
655 int Array_len( const char* name );
657 bool Is_bool_ele( const char* name, int eidx );
658 bool Is_null_ele( const char* name, int eidx );
659 bool Is_string_ele( const char* name, int eidx );
660 bool Is_value_ele( const char* name, int eidx );
662 bool Bool_ele( const char* name, int eidx );
663 std::string String_ele( const char* name, int eidx );
664 float Value_ele( const char* name, int eidx );
667 For each of these functions the ``eidx`` is the zero based
668 element index which is to be tested or selected.
674 An array containing blobs, rather than simiple field value
675 pairs, the blob must be selected prior to using it, just as a
676 sub-blob needed to be selected. The ``Set_blob_ele`` function
677 is used to do this and has the following prototype:
681 bool Set_blob_ele( const char* name, int eidx );
684 As with selecting a sub-blob, an unset must be preformed
685 before selecting the next blob. Figure 11 illustrates how
686 these functions can be used to read and print values from the
692 { "name": "Fred Flinstone", "member_num": 42 },
693 { "name": "Barney Rubble", "member_num": 48 },
694 { "name": "Larry K Slate", "member_num": 22 },
695 { "name": "Kyle Limestone", "member_num": 49 }
698 Figure 11: Json array containing blobs.
707 len = jh->Array_len( (char *) "members" );
708 for( i = 0; i < len; i++ ) {
709 jh->Set_blob_ele( (char *) "members", i ); // select blob
711 mname = jh->String( (char *) "name" ); // read values
712 mnum = jh->Value( (char *) "member_num" );
713 fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum );
715 jh->Unset_blob(); // back to root
718 Figure 12: Code to process the array of blobs.
725 The following sections contain several example programmes
726 which are written on top of the C++ framework.
732 The RMR dump application is an example built on top of the
733 C++ xApp framework to both illustrate the use of the
734 framework, and to provide a useful diagnostic tool when
735 testing and troubleshooting xApps.
737 The RMR dump xApp isn't a traditional xApp inasmuch as its
738 goal is to listen for message types and to dump information
739 about the messages received to the TTY much as
740 ``tcpdump`` does for raw packet traffic. The full source
741 code, and Makefile, are in the ``examples`` directory of the
744 When invoked, the RMR dump program is given one or more
745 message types to listen for. A callback function is
746 registered for each, and the framework ``Run()`` function is
747 invoked to drive the process. For each recognised message,
748 and depending on the verbosity level supplied at program
749 start, information about the received message(s) is written
750 to the TTY. If the forwarding option, -f, is given on the
751 command line, and an appropriate route table is provided,
752 each received message is forwarded without change. This
753 allows for the insertion of the RMR dump program into a flow,
754 however if the ultimate receiver of a message needs to reply
755 to that message, the reply will not reach the original
756 sender, so RMR dump is not a complete "middle box"
759 The following is the code for this xAPP. Several functions,
760 which provide logic unrelated to the framework, have been
761 omitted. The full code is in the framework repository.
771 #include "ricxfcpp/xapp.hpp"
774 Information that the callback needs outside
775 of what is given to it via parms on a call
779 int vlevel; // verbosity level
780 bool forward; // if true, message is forwarded
781 int stats_freq; // header/stats after n messages
782 std::atomic<long> pcount; // messages processed
783 std::atomic<long> icount; // messages ignored
784 std::atomic<int> hdr; // number of messages before next header
787 // ----------------------------------------------------------------------
792 void dump( unsigned const char* buf, int len ) {
797 fprintf( stdout, "<RD> 0000 | " );
799 for( i = 0; i < len; i++ ) {
800 cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
801 fprintf( stdout, "%02x ", buf[i] );
805 fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
812 for( ; i > 0; i-- ) {
813 fprintf( stdout, " " );
816 fprintf( stdout, " | %s\\n", cheater );
821 generate stats when the hdr count reaches 0. Only one active
822 thread will ever see it be exactly 0, so this is thread safe.
824 void stats( cb_info_t& cbi ) {
825 int curv; // current stat trigger value
829 if( curv == 0 ) { // stats when we reach 0
830 fprintf( stdout, "ignored: %ld processed: %ld\\n",
831 cbi.icount.load(), cbi.pcount.load() );
832 if( cbi.vlevel > 0 ) {
833 fprintf( stdout, "\\n %5s %5s %2s %5s\\n",
834 "MTYPE", "SUBID", "ST", "PLLEN" );
837 cbi.hdr = cbi.stats_freq; // reset must be last
841 void cb1( Message& mbuf, int mtype, int subid, int len,
842 Msg_component payload, void* data ) {
846 if( (cbi = (cb_info_t *) data) == NULL ) {
851 stats( *cbi ); // gen stats & header if needed
853 if( cbi->vlevel > 0 ) {
854 fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
855 mtype, subid, mbuf.Get_state(), len );
857 if( cbi->vlevel > 1 ) {
858 dump( payload.get(), len > 64 ? 64 : len );
863 // forward with no change to len or payload
864 mbuf.Send_msg( Message::NO_CHANGE, NULL );
869 registered as the default callback; it counts the
870 messages that we aren't giving details about.
872 void cbd( Message& mbuf, int mtype, int subid, int len,
873 Msg_component payload, void* data ) {
876 if( (cbi = (cb_info_t *) data) == NULL ) {
884 // forward with no change to len or payload
885 mbuf.Send_msg( Message::NO_CHANGE, NULL );
889 int main( int argc, char** argv ) {
890 std::unique_ptr<Xapp> x;
891 char* port = (char *) "4560";
892 int ai = 1; // arg processing index
894 int ncb = 0; // number of callbacks registered
898 cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
901 cbi->stats_freq = 10;
904 // very simple flag parsing (no error/bounds checking)
906 if( argv[ai][0] != '-' ) { // break on first non-flag
910 // very simple arg parsing; each must be separate -x -y not -xy.
911 switch( argv[ai][1] ) {
912 case 'f': // enable packet forwarding
916 case 'p': // define port
921 case 's': // stats frequency
922 cbi->stats_freq = atoi( argv[ai+1] );
923 if( cbi->stats_freq < 5 ) { // enforce sanity
929 case 't': // thread count
930 nthreads = atoi( argv[ai+1] );
937 case 'v': // simple verbose bump
941 case 'V': // explicit verbose level
942 cbi->vlevel = atoi( argv[ai+1] );
947 fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
948 fprintf( stderr, "usage: %s [-f] [-p port] "
949 "[-s stats-freq] [-t thread-count] "
950 "[-v | -V n] msg-type1 ... msg-typen\\n",
952 fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
953 fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
954 "1 message info 2 payload dump\\n" );
961 cbi->hdr = cbi->stats_freq;
962 fprintf( stderr, "<RD> listening on port: %s\\n", port );
964 // create xapp, wait for route table if forwarding
965 x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
967 // register callback for each type on the command line
969 mtype = atoi( argv[ai] );
971 fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
972 x->Add_msg_cb( mtype, cb1, cbi );
977 fprintf( stderr, "<RD> no message types specified on the command line\\n" );
981 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
983 fprintf( stderr, "<RD> starting driver\\n" );
986 // return from run() is not expected, but some compilers might
987 // compilain if there isn't a return value here.
991 Figure 13: Simple callback application.
997 This sample programme implements a simple message listener
998 which registers three callback functions to process two
999 specific message types and a default callback to handle
1000 unrecognised messages.
1002 When a message of type 1 is received, it will send two
1003 response messages back to the sender. Two messages are sent
1004 in order to illustrate that it is possible to send multiple
1005 responses using the same received message.
1007 The programme illustrates how multiple listening threads can
1008 be used, but the programme is **not** thread safe; to keep
1009 this example as simple as possible, the counters are not
1010 locked when incremented.
1017 #include "ricxfcpp/message.hpp"
1018 #include "ricxfcpp/msg_component.hpp"
1019 #include "ricxfcpp/xapp.hpp"
1021 // counts; not thread safe
1026 long cb1_lastts = 0;
1029 // respond with 2 messages for each type 1 received
1030 void cb1( Message& mbuf, int mtype, int subid, int len,
1031 Msg_component payload, void* data ) {
1035 // illustrate that we can use the same buffer for 2 rts calls
1036 mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
1037 mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
1042 // just count messages
1043 void cb2( Message& mbuf, int mtype, int subid, int len,
1044 Msg_component payload, void* data ) {
1048 // default to count all unrecognised messages
1049 void cbd( Message& mbuf, int mtype, int subid, int len,
1050 Msg_component payload, void* data ) {
1054 int main( int argc, char** argv ) {
1056 char* port = (char *) "4560";
1057 int ai = 1; // arg processing index
1060 // very simple flag processing (no bounds/error checking)
1061 while( ai < argc ) {
1062 if( argv[ai][0] != '-' ) {
1066 switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y
1073 nthreads = atoi( argv[ai+1] );
1081 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1082 fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
1084 x = new Xapp( port, true );
1085 x->Add_msg_cb( 1, cb1, NULL ); // register callbacks
1086 x->Add_msg_cb( 2, cb2, NULL );
1087 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
1089 x->Run( nthreads ); // let framework drive
1090 // control should not return
1093 Figure 14: Simple callback application.
1100 This is another very simple application which demonstrates
1101 how an application can control its own listen loop while
1102 sending messages. As with the other examples, some error
1103 checking is skipped, and short cuts have been made in order
1104 to keep the example small and to the point.
1117 #include "ricxfcpp/xapp.hpp"
1119 extern int main( int argc, char** argv ) {
1120 std::unique_ptr<Xapp> xfw;
1121 std::unique_ptr<Message> msg;
1122 Msg_component payload; // special type of unique pointer to the payload
1128 int response_to = 0; // max timeout wating for a response
1129 char* port = (char *) "4555";
1131 int rmtype; // received message type
1132 int delay = 1000000; // mu-sec delay; default 1s
1135 // very simple flag processing (no bounds/error checking)
1136 while( ai < argc ) {
1137 if( argv[ai][0] != '-' ) {
1141 // we only support -x so -xy must be -x -y
1142 switch( argv[ai][1] ) {
1143 // delay between messages (mu-sec)
1145 delay = atoi( argv[ai+1] );
1154 // timeout in seconds; we need to convert to ms for rmr calls
1156 response_to = atoi( argv[ai+1] ) * 1000;
1163 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1164 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1166 // get an instance and wait for a route table to be loaded
1167 xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1168 msg = xfw->Alloc_msg( 2048 );
1170 for( i = 0; i < 100; i++ ) {
1176 // we'll reuse a received message; get max size
1177 sz = msg->Get_available_size();
1179 // direct access to payload; add something silly
1180 payload = msg->Get_payload();
1181 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1183 // payload updated in place, prevent copy by passing nil
1184 if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) {
1185 fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1188 // receive anything that might come back
1189 msg = xfw->Receive( response_to );
1191 rmtype = msg->Get_mtype();
1192 payload = msg->Get_payload();
1193 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1194 rmtype, (char *) payload.get() );
1196 msg = xfw->Alloc_msg( 2048 );
1205 Figure 15: Simple looping sender application.