RTL Documentation

1.0

Introduction

Welcome to RTL, the Remedy Template Library. RTL makes developing Remedy AR System integrations using Remedy's C API much easier and less error prone. Your development environment remains C++, and the only dependency is a C++ compiler with a standards-compliant implementation of the Standard Template Library, part of the C++ language specification.

Typical integrations developed using RTL have as much as an 80% reduction in code, are much simpler, and in most cases have better performance. At a minimum, code using RTL is far less prone to coding mistakes and memory leaks.

Details

How does RTL make Remedy integration easier, faster, and simpler?

Much of the development cost with programs using Remedy's C API is with "overhead" - memory management, error handling, and working with collections. Most modern languages largely do this work for you. Memory management is handled by encapsulation or garbage collection, error handling is simplified with exceptions, and many useful collections are available through the C++ STL, Java, or .NET.

RTL brings all of these benefits to the Remedy C API by leveraging the power of encapsulation, exception handling, and the C++ Standard Template Library.

RTL does not lock you in, or prevent you from accessing the C API. You can easily intermix calls using RTL and the C API anywhere in your code. To share RTL's session and use its ARControlStruct, simply call Server::getControlStruct(). Also, every class inheriting from AssignableT supports casting to the equivalent AR structure, and assignment from it.

How about an example?

Here's how to make an API call to find out what the server's name and database type are using RTL:


 try {
   Server server("localhost", "Demo", "");
   ServerInfoMap serverInfo;
   server.getServerInfo(serverInfo, ServerInfoRequestList().add(AR_SERVER_INFO_SERVER_NAME).add(AR_SERVER_INFO_DB_TYPE));
   cout << "Server " << serverInfo.get(AR_SERVER_INFO_SERVER_NAME).toString().c_str() << " is using database " << 
       serverInfo.get(AR_SERVER_INFO_DB_TYPE).toString().c_str() << "\n";
 } catch (Exception &err) {
    cerr << "Error: " << err.toString().c_str();
    return 2;
 }
 return 0;
 

Now isn't that easy? Here is the same example using Remedy's C API:


 ARControlStruct control;
 memset(&control, 0, sizeof(control));
 strcpy(control.server, "localhost");
 strcpy(control.user, "Demo");
 ARStatusList statusList = {0, NULL};
 unsigned int ret = ARInitialization(&control, &statusList);
 if (ret >= AR_RETURN_ERROR) {
   fprintf(stderr, "Error: %s\n", statusList.statusList ? statusList.statusList[0].messageText : "");
   FreeARStatusList(&statusList, FALSE);
   return 2;
 }
 FreeARStatusList(&statusList, FALSE);

 unsigned int uintList[2] = { AR_SERVER_INFO_SERVER_NAME, AR_SERVER_INFO_DB_TYPE };
 ARServerInfoRequestList requestList = {2, uintList};

 ARServerInfoList serverInfo = {0, NULL};

 ret = ARGetServerInfo(&control, &requestList, &serverInfo, &statusList);

 if (ret >= AR_RETURN_ERROR) {
   fprintf(stderr, "Error: %s\n", statusList.statusList ? statusList.statusList[0].messageText : "");
   FreeARStatusList(&statusList, FALSE);
   return 2;
 }
 FreeARStatusList(&statusList, FALSE);

 printf("Server ");
 bool found = false;
 // find the server name by looking up in the results
 for (int i=0; !found && i < serverInfo.numItems; i++) {
    if (serverInfo.serverInfoList[i].operation == AR_SERVER_INFO_SERVER_NAME 
         && serverInfo.serverInfoList[i].value.dataType == AR_DATA_TYPE_CHAR 
         && serverInfo.serverInfoList[i].value.u.charVal) {
         printf("%s ", serverInfo.serverInfoList[i].value.u.charVal);
         found = true;
    }
 }
 if (!found) {
   fprintf(stderr, "Object not found in collection.\n");
   FreeARServerInfoList(&serverInfo, FALSE);
   ret = ARTermination(&control, &statusList);
   if (ret >= AR_RETURN_ERROR) {
     fprintf(stderr, "Error: %s\n", statusList.statusList ? statusList.statusList[0].messageText : "");
     FreeARStatusList(&statusList, FALSE);
     return 2;
   }
   FreeARStatusList(&statusList, FALSE);
   return 2;
 }

 // same with db type
 found = false;
 for (i=0; !found && i < serverInfo.numItems; i++) {
    ;
    if (serverInfo.serverInfoList[i].operation == AR_SERVER_INFO_DB_TYPE
         && serverInfo.serverInfoList[i].value.dataType == AR_DATA_TYPE_CHAR 
         && serverInfo.serverInfoList[i].value.u.charVal) {
         printf("is using database %s \n", serverInfo.serverInfoList[i].value.u.charVal);
         found = true;
    }
 }
 if (!found) {
   fprintf(stderr, "Object not found in collection.\n");
   FreeARServerInfoList(&serverInfo, FALSE);
   ret = ARTermination(&control, &statusList);
   if (ret >= AR_RETURN_ERROR) {
     fprintf(stderr, "Error: %s\n", statusList.statusList ? statusList.statusList[0].messageText : "");
     FreeARStatusList(&statusList, FALSE);
     return 2;
   }
   FreeARStatusList(&statusList, FALSE);
   return 2;
 }
 FreeARServerInfoList(&serverInfo, FALSE);
 ret = ARTermination(&control, &statusList);
 if (ret >= AR_RETURN_ERROR) {
   fprintf(stderr, "Error: %s\n", statusList.statusList ? statusList.statusList[0].messageText : "");
   FreeARStatusList(&statusList, FALSE);
   return;
 }
 FreeARStatusList(&statusList, FALSE);
 

How is the code equivalent? Does it really do everything the C code does?

Yes, the code is equivalent. ARInitialization is done by the constructor of Server, as is set up of the control structure. ARTermination is done by the destructor of Server. All error handling is done with the try/catch, including if a call to ServerInfoMap::get(ARInternalId) is missing the item being requested! All memory management is done by class constructors, destructors, and assignment operators - in all likelihood you will never call new, delete, malloc, or free when using RTL.

Common Questions

Don't I have to be a C++ expert, learn STL, and understand templates?

Not really...If you look at the above code example, you'll find that using RTL is far less technical than using the C API. There are also plenty of code examples in the documentation for each method. Because all classes follow some simple rules, they behave exactly like intrinsic data types (specifically, they all have assignment operators and copy constructors). Using RTL classes is a lot like using objects in scripting languages or Java, although assignment results in a copy of an object. I'll wager that it is far easier to understand how to use these classes than to understand pointers and memory management in C.

You do need to learn to use an STL iterator to loop over objects in a collection. However, that is straightforward, and code samples exist for all methods on the Server object, many with examples of iterating over a returned collection. See Server::getListSchema for an example.

Is RTL faster or slower? Why?

Both, depending on your required functionality. But in practice, RTL is unlikely to be measurably slower in any case, and in many cases can be dramatically faster.

Why? Data from AR System API calls is immediately copied into classes by RTL, and any data allocated by the C API is immediately freed. There is a very small performance cost related to this copying of data in memory. All of this happens as a call is made inside the Server class.

However, looking up values in collections can be much faster using RTL, because collections used for lookups are implemented using std::map where appropriate, such as when looking up field values (FieldValueMap), or display properties (DisplayInstanceMap, PropMap). A lookup using std::map is an O(log N) operation, whereas a a lookup into an AR System C API structure is an O(n) operation. The difference therefore becomes more and more dramatic depending on the size of the collection. For operations such as finding the label of a field from a display instance list, where there are multiple lookups in sequence, this will be be even more imporant (e.g. first finding the display instance, then finding the label property.)

I tested the above examples by running 1000 iterations on my own machine, with a local AR System Server, and evaluating the results using clock(), which has millisecond precision. Each iteration averaged 10.1 milliseconds using RTL. Using the C example, each iteration averaged 10.2 milliseconds. The performance variation here is not statistically significant, but for real world examples with collection lookups, RTL is likely to be faster, sometimes dramatically so, and it is unlikely ever to be measureably slower.

As all collection classes inherit from the STL collections (std::list, std::map, or std::vector), all of the standard STL functionality is available and can be used. For example, to get a list of forms (schemas) from the server, sort them (they don't come back sorted automatically!), and print them out, do this:


 NameList forms;
 server.getListSchema(forms);
 forms.sort();
 NameList::iterator iter = forms.begin();
 for (; iter != forms.end(); iter++) {
   cout << (*iter).getName().c_str();
 }
 

Now beat that using the C API! :)


Generated on Thu Dec 29 16:20:28 2005 for RTL by  doxygen 1.4.5