Working With Messages


This section explains how the TipcMsg and TipcMt classes are used in a C++ program and provides example programs that show how the classes are used.

TipcMsg Construction

There are several ways of constructing a TipcMsg object. The construction method to use depends on what information is available at the time the object is being constructed.

Using A Simple Constructor

The simplest constructor of the TipcMsg class is:

TipcMsg::TipcMsg()  

This constructor creates a vacant TipcMsg object that is later initialized with a C type T_IPC_MSG or a non-vacant TipcMsg object using operator=(). For example:

// Create a couple of vacant TipcMsg objects using the simple 
// TipcMsg constructor 
TipcMsg vacant_msg_obj_1, vacant_msg_obj_2; 
 
// Initialize vacant object using operator=(). 
TipcMsg non_vacant_msg_obj(T_MT_TIME); 
vacant_msg_obj_1 = non_vacant_msg_obj; 
 
// Initialize vacant object using the Create member function. 
vacant_msg_obj_2.Create(T_MT_INFO); 

Using A TipcMt Object

A TipcMsg object is created with a TipcMt object using the TipcMsg::TipcMsg(const TipcMt& mt_obj) constructor. For example:

const T_INT4 MY_MSG_TYPE = 100; 
 
TipcMt mt_obj("my_msg_type", MY_MSG_TYPE, "int4 real4"); 
 
TipcMsg msg(mt_obj); 

Using The Convenience Constructor

A TipcMsg object is created using what is called the convenience constructor. For example:

TipcMsg::TipcMsg(T_INT4 mt_num, 
                 T_STR destination = (T_STR)NULL, 
                 T_STR sender = (T_STR)NULL); 

This constructor is so named because it can construct a TipcMsg object without using a C type T_IPC_MT variable or a TipcMt object, thus saving the steps necessary in assigning the C type T_IPC_MT variable or instantiating a TipcMt object prior to the construction of the TipcMsg object. The constructor also initializes a few message properties. The only required argument is mt_num, the message type number. The other two parameters have default values and thus can be optionally supplied. The default values for destination and sender are null string pointers. For example:

// create a few TipcMsg objects. 
TipcMsg msg1(T_MT_NUMERIC_DATA); 
TipcMsg msg2(T_MT_INFO, "/stocks"); 
TipcMsg msg3(T_MT_CONTROL, "/system", "/admin"); 

Using a Message Type Name

A TipcMsg object is created with a message type name using:

TipcMsg::TipcMsg(mt_name) 

Like the convenience constructor above, this constructor can construct a TipcMsg object without using a C type T_IPC_MT variable or a TipcMt object. For example:

// create a TipcMsg object using a message type name 
TipcMsg msg("info"); 

Using The Copy Constructor

A TipcMsg object is created for another TipcMsg object using the copy constructor:

TipcMsg::TipcMsg(msg_obj) 

This constructor is invoked when passing a TipcMsg by value to a function, when a TipcMsg object is returned by a function, or when an explicit copy is made. For example:

TipcMsg userCode(TipcMsg b)  // pass by value: copy main()’s a to b 
{ 
    TipcMsg c(b);      // explicit copy: copy from b to c 
    return c;          // return by value: copy from c to main()’s d 
} 
int main() 
{ 
    TipcMsg a; 
    TipcMsg d = userCode(a); 
} 

When this constructor is used, the message reference count increments by one.

Using A C Type T_IPC_MSG

A TipcMsg object is created with a C type T_IPC_MSG variable using the constructor:

TipcMsg::TipcMsg(T_IPC_MSG c_type_msg) 
 

For example:

T_IPC_MSG msg; 
 
// create a C type T_IPC_MSG message 
msg = TipcMsgCreate(TipcMtLookup("info")); 
 
// construct a TipcMsg object using a C type T_IPC_MSG 
TipcMsg msg_object(msg); 

When this constructor is used, the message reference count increments by one.

Appending and Accessing TipcMsg Data Fields

There are two means of appending and accessing data fields of non-vacant TipcMsg objects. The first way is to use overloaded TipcMsg::Append() and TipcMsg::Next() functions. These functions duplicate the C API TipcMsgAppend* and TipcMsgNext* functionality. An alternative means is to use the overloaded C++ insertion and extraction operators (operator<<() and operator>>(), respectively). To illustrate the two methods, a program is implemented twice in this example, using one of the two methods each time.

Using Append and Next Functions

This example illustrates the usage of the Append() and Next() member functions of the TipcMsg class. These member functions are overloaded and accept many types of arguments. These functions duplicate the functionality of the TipcMsgAppend* and TipcMsgNext* function sets in the C API. See TipcMsg on page 110 for more information on these member functions.

The source code file for this example is located in this directory:

UNIX:
$RTHOME/examples/cxxipc 
OpenVMS:
RTHOME:[EXAMPLES.CXXIPC] 
Windows:
%RTHOME%\examples\cxxipc 

In some cases, C++ literals must be cast to an appropriate data type so that the C++ compiler selects the correct overloaded Append member function. For example, 33.4534 could be interpreted as a T_REAL4 or a T_REAL8 by the compiler. To clear the ambiguity, use either (T_REAL4)33.4534 or (T_REAL8)33.4534.

// msg1.cxx -- messages example using Append and Next 
#include <rtworks/cxxipc.hxx> 
 
int main() 
{ 
  T_STR str_val; 
  T_REAL8 real8_val; 
 
  TutOut("Create the message.\n"); 
  TipcMsg msg(T_MT_NUMERIC_DATA, "/_saratoga", "/thermal"); 
  if (!msg) { 
    TutOut("Could not create message.\n"); 
    return T_EXIT_FAILURE; 
  } 
  TutOut("Append fields.\n"); 
  if (!msg.Append("voltage")) { 
    TutOut("Could not append first field.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  // A cast to T_REAL8 here is to ensure that the C++ compiler 
  // selects the correct overloaded Append member function. 
   
if (!msg.Append((T_REAL8)33.4534)) { 
    TutOut("Could not append second field.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!msg.Append("switch_pos")) { 
    TutOut("Could not append third field.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!msg.Append((T_REAL8)0.0)) { 
    TutOut("Could not append fourth field.\n"); 
    return T_EXIT_FAILURE; 
  } 
   
  TutOut("Access fields.\n"); 
  if (!msg.Current(0)) { 
    TutOut("Could not set current field to first field.\n"); 
    return T_EXIT_FAILURE; 
  } 
   
  if (!msg.Next(&str_val)) { 
    TutOut("Could not read first field.\n"); 
    return T_EXIT_FAILURE ; 
  } 
     
    if (!msg.Next(&real8_val)) { 
    TutOut("Could not read second field.\n"); 
    return T_EXIT_FAILURE; 
  } 
  TutOut("%s = %s\n", str_val, TutRealToStr(real8_val)); 
  if (!msg.Next(&str_val)) { 
    TutOut("Could not read third field.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!msg.Next(&real8_val)) { 
    TutOut("Could not read fourth field.\n"); 
    return T_EXIT_FAILURE; 
  } 
  TutOut("%s = %s\n", str_val, TutRealToStr(real8_val)); 
 
  // msg is destroyed when TipcMsg goes out of scope and 
  // the TipcMsg destructor is called. 
  return T_EXIT_SUCCESS;  // all done   
 
}  // main   

Using Insertion and Extraction Operators

This example illustrates the usage of insertion and extraction operators (operator<<() and operator>>(), respectively) of the TipcMsg class. These operators are overloaded and accept many types of arguments. These operators are an alternative means of appending and accessing the data fields of a message, and can be used instead of the Append() and Next() member functions. Please refer to TipcMsg for more information on these operators.

The source code file for this example is located in this directory:

UNIX:
$RTHOME/examples/cxxipc 
OpenVMS:
RTHOME:[EXAMPLES.CXXIPC] 
Windows:
%RTHOME%\examples\cxxipc 

As with the Append() member functions, some literals must be cast to an appropriate data type so that the C++ compiler selects the correct overloaded insertion operator. This example shows how this was applied for a T_REAL8 cast of 33.4534.

// msg2.cxx -- messages example using insertion  
// and extraction operators 
#include <rtworks/cxxipc.hxx> 
 
int main() 
{ 
  T_STR str_val; 
  T_REAL8 real8_val; 
 
  TutOut("Create the message.\n"); 
  TipcMsg msg(T_MT_NUMERIC_DATA, "/_saratoga_5415", "/thermal"); 
  if (!msg) { 
    TutOut("Could not create message.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  TutOut("Append fields.\n"); 
 
  // A cast to T_REAL8 here is to ensure that the C++ compiler 
  // selects the correct overloaded insertion operator. 
  msg << "voltage" << (T_REAL8)33.4534 
      << "switch_pos" << (T_REAL8)0.0 << Check; 
  if (!msg) { 
    TutOut("Could not append data fields to message\n"); 
    return T_EXIT_FAILURE; 
  } 
  TutOut("Access fields.\n"); 
  if (!msg.Current(0)) { 
    TutOut("Could not set current field to first field.\n"); 
    return T_EXIT_FAILURE; 
  } 
  msg >> str_val >> real8_val >> Check; 
  if (!msg) { 
    TutOut("Could not read data fields from message.\n"); 
    return T_EXIT_FAILURE; 
  } 
  TutOut("%s = %s\n", str_val, TutRealToStr(real8_val)); 
   
  msg >> str_val >> real8_val >> Check; 
  if (!msg) { 
    TutOut("Could not read data fields from message.\n"); 
    return T_EXIT_FAILURE; 
  } 
  TutOut("%s = %s\n", str_val, TutRealToStr(real8_val)); 
 
  // msg is destroyed when TipcMsg goes out of scope and 
  // the TipcMsg destructor is called. 
  return T_EXIT_SUCCESS;  // all done   
 
}  // main   

Running Message Programs

Compile and link msg1 and msg2 (see Compiling, Linking, and Running for generic instructions). Use these commands to run the programs:

UNIX:
$ msg1.x  
$ msg2.x  
OpenVMS:
$ run msg1.exe 
$ run msg2.exe 
Windows:
$ msg1.exe 
$ msg2.exe 

The output from the msg1 and msg2 process is similar to this output:

Create the message. 
Append fields. 
Access fields. 
voltage = 33.4534 
switch_pos = 0 

Using Array Fields

Array fields are appended and accessed in two ways just like scalar data types. One way is to use the overloaded Append() and Next() member functions of the TipcMsg class. Like the C API, the Append() member function requires the number of elements in the array as a parameter. For example:

T_REAL8 real8_array[10]; // can hold up to 10 values 
 
real8_array[0] = 3.0; 
real8_array[1] = 4.0; 
real8_array[2] = 55.75; 
if (!msg.Append(real8_array, 3)) { 
    // error 
} 

Array fields can be accessed using the Next() member function. Like the C API, Next() requires a parameter where it stores the number of elements in the array. For example:

T_REAL8 *real8_array; 
T_INT4 array_size; 
 
if (!msg.Next(&real8_array, &array_size)) { 
    // error 
} 

The second way of appending and accessing array fields is using the insertion and extraction operators. When using these operators, the SetSize() and GetSize() manipulators are employed to set and retrieve, respectively, the number of elements in the array field. To append an array, use SetSize() and operator<<(). For example:

T_REAL8 real8_array[10]; // can hold up to 10 values 
 
real8_array[0] = 3.0; 
real8_array[1] = 4.0; 
real8_array[2] = 55.75; 
 
msg << SetSize(3) << real8_array << Check; 
if (!msg) { 
    // error 
} 

Note that the Check manipulator is used in the function chain to set the error status of the TipcMsg object, msg. To access an array, use operator>>() and GetSize(). For example:

T_REAL8 *real8_array; 
T_INT4 array_size; 
 
msg >> real8_array >> GetSize(&array_size) >> Check; 
if (!msg) { 
    // error 
} 

The order in which the SetSize() and GetSize() manipulators are called is critical. When inserting an array, the SetSize() manipulator must be used prior to inserting the array. When extracting an array, the GetSize() manipulator must be used after extracting the array. If the manipulators are used improperly, unpredictable results may occur.

Message Array Fields

A message may contain a field that is an array of messages. This example shows how a message array can be declared in C:

T_IPC_MSG msg_array[3]; 

A message array is inserted into the message with TipcMsgAppendMsgArray(), and it is extracted with TipcMsgNextMsgArray(). See the detailed descriptions in the TIBCO SmartSockets Application Programming Interface reference for examples of how these functions are used.

In C++, the equivalent of a T_IPC_MSG array is the TipcMsg array. An array of TipcMsg objects can be inserted into a message using either TipcMsg::Append() or the TipcMsg insertion operator. Likewise, a TipcMsg array can be extracted from a message using either TipcMsg::Next() or the TipcMsg extraction operator.

Inserting a TipcMsg array into a message in C++ is simple. For example:

TipcMsg msg1("T_MT_INFO"); 
TipcMsg msg2("T_MT_INFO"); 
TipcMsg msg_array[3]; 
 
msg_array[0].Create(T_MT_NUMERIC_DATA); 
msg_array[1].Create(T_MT_NUMERIC_DATA); 
msg_array[2].Create(T_MT_CONTROL); 
 
// Use the insertion operator to insert an array 
msg1 << SetSize(3) << msg_array << Check; 
if (!msg1) { 
    // error 
} 
// Use the Append member function to insert an array 
msg2.Append(msg_array, 3); 
if (!msg2) { 
    // error 
} 

Both the insertion operator and the Append() member function take a TipcMsg array, convert it to a T_IPC_MSG array, and append the T_IPC_MSG array to the T_IPC_MSG message managed by a TipcMsg object.

Extracting a message is not as straightforward as inserting one for two reasons. First, the size of a message array is unknown until after it is extracted. Second, the T_IPC_MSG array extracted from a message must be converted into a TipcMsg array. Therefore, both the extraction operator and the Next() member function extract a T_IPC_MSG array from a message, determine the size of the array, and then dynamically allocate a TipcMsg array to hold the elements of the extracted T_IPC_MSG array. The address of the allocated TipcMsg array is returned to the caller. The burden of deallocating a TipcMsg array is on the caller. This is an example of extracting a TipcMsg array from a message:

TipcConnClient conn("tcp:_node:5252"); 
TipcMsg msg; 
TipcMsg *msg_array; 
T_INT4 size; 
 
msg = conn.Next(10.0); 
if (!conn) { 
    // error 
} 
// set the message to the first field 
msg.Current(0); 
 
// extract a message array using the extraction operator 
msg >> &msg_array >> GetSize(&size) >> Check; 
  if (!msg) { 
    // error 
} 
// print the contents of the message array 
for (T_INT4 i = 0; i < size; i++) { 
    msg_array[i].Print(TutOut); 
} 
 
// delete the message array 
delete[] msg_array; 
 
// set the message to the first field 
msg.Current(0); 
 
// extract a message array using the Next member function 
msg.Next(&msg_array, &size); 
if (!msg) { 
    // error 
} 
 
// print the contents of the message array 
for (T_INT4 i = 0; i < size; i++) { 
    msg_array[i].Print(TutOut); 
} 
 
// delete the message array 
delete[] msg_array; 

Deleting Extracted TipcMsg Arrays

When a TipcMsg array is deleted or goes out of scope, the TipcMsg destructor is called on each element of the array. The destructor always passes the internal T_IPC_MSG variable managed by each TipcMsg array element to TipcMsgDestroy() unless the internal T_IPC_MSG is a read-only message. In other words, whenever a TipcMsg array is destroyed, the internal T_IPC_MSG variables managed by each element of the array is passed to TipcMsgDestroy(). When a TipcMsg array returned by a TipcMsg extraction routine is destroyed, the internal T_IPC_MSG variables managed by the array are passed to TipcMsgDestroy(). The reason for this is that a message or message array extracted from a message is read-only.

When a message is destroyed, any messages and message arrays within the original message are also destroyed.

TipcMsg and T_IPC_MSG Reference Counts

The TipcMsg class makes use of the T_IPC_MSG reference count mechanism. The reference count of a message increments when creating and assigning TipcMsg objects. This saves memory, because two or more TipcMsg objects can reference the same T_IPC_MSG variable.

When a T_IPC_MSG variable is converted to a TipcMsg object, the appropriate constructor is used and the reference count of the message is incremented. For example:

T_IPC_MSG c_msg; 
 
c_msg = TipcSrvNext(10.0); 
if (c_msg != NULL) { 
 // error 
} 
// At this point the message pointed to by c_msg has a reference  
// count of 1. 
 
TipcMsg msg(c_msg); 
// Now the message has a reference count of 2. 

However, when a TipcMsg object is converted to a T_IPC_MSG variable the reference count remains the same. For example:

T_IPC_MSG c_msg; 
 
TipcMsg msg(T_MT_NUMERIC_DATA); 
// At this point the reference count of the message is 1. 
 
c_msg = msg; 
// At this point the reference count of the message is still 1. 

Whenever a TipcMsg object is assigned a new message, the original message managed by the TipcMsg object is passed to the C API TipcMsgDestroy() function. The effect of this is to decrement the reference count of the original message by one before assigning the new message to the TipcMsg object. There are no corresponding reference counts for TipcMt objects or objects that manage connection-oriented information (such as TipcConnClient or TipcConnServer).

If the reference count of the original message is one (1) at the time of the assignment, then TipcMsgDestroy() physically destroys the original message. See the detailed description of TipcMsgDestroy in the TIBCO SmartSockets Application Programming Interface reference for more information.


TIBCO SmartSockets™ cxxipc Class Library
Software Release 6.8, July 2006
Copyright © TIBCO Software Inc. All rights reserved
www.tibco.com