Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

gSOAP & Web Services


Serializing Nonserializable Types

When a particular data type is defined elsewhere (such as in a library's header file) and its definition cannot be moved into the gSOAP header without duplicating it, you need to qualify it as volatile. The volatile qualification of a data type ensures that the type is not redefined in the gSOAP compiler's output. The gSOAP compiler generates new header files soapStub.h and soapH.h in which all of the data types defined in the gSOAP header file specification are copied, except types qualified as volatile. If you wouldn't qualify a predefined data type volatile, compiling the generated source code would result in a redefinition error.

In fact, the volatile qualifier lets you serialize predefined types that would otherwise not be serializable (that is, should otherwise be declared extern). For example, suppose you have a class with a public struct tm member, as in:

class MyClass
{ public:
   struct tm created; // oops, how to serialize?
   ...
};

The tm struct is declared in <time.h> and its definition may vary from one platform to another. To serialize the member, you define volatile struct tm in the header file with a selection of fields you want serialized:

volatile struct tm
{ int tm_sec;	// seconds (0 - 60)
  int tm_min;	// minutes (0 - 59)
  int tm_hour;	// hours (0 - 23)
  int tm_mday;	// day of month (1 - 31)
  int tm_mon;	// month of year (0 - 11)
  int tm_year;	// year - 1900
  int tm_wday;	// day of week (Sunday = 0)
  int tm_yday;	// day of year (0 - 365)
  int tm_isdst;	// is summer time in effect?
  char* tm_zone;	// abbreviation of timezone name
  long tm_gmtoff;	// offset from UTC in seconds
};

Another use of volatile can be found for classes. Classes are augmented with virtual serialization methods by the gSOAP compiler (they're redefined with additional methods in the generated soapStub.h file). While this approach provides a powerful mechanism to serialize polymorphic objects, there may be situations in which it is undesirable to change a class definition. The role of the volatile qualifier is to prevent class redefinition and to keep the original definition intact.

Developing a Web Service

With this basic understanding of XML serialization with gSOAP, take a closer look at developing a web service application with gSOAP. The goal here is to implement an object collision-detection service that takes a collection of geometric objects and determines how many collide with each other on a 2D plane.

Of course, you can implement a collision-detection algorithm locally, but for the sake of exposition, assume you want to expose a collision detector as a service. This example illustrates the use of primitive types, class hierarchies, containers, and operations to send and receive the objects. To keep the system open ended, I define the objects in an extensible class hierarchy starting with the simplest form, a point in 2D space declared in point.h in Listing Three. A Point object has a serializable x- y-coordinate pair and a virtual radius method to describe the total size of the object in terms of a simple circular shape. The virtual distance method measures the geometric distance from another object's center point. Because the gSOAP compiler does not parse method code, all method implementations must be provided in a separate source-code file point.cpp in Listing Four.

Listing Three

// file: point.h
class Point
{ public:
   float x, y;
   Point();
   Point(float, float);
   virtual float radius() const;
   virtual float distance(const Point&) const;
};

Listing Four

// file: point.cpp
#include "soapH.h"
Point::Point() : x(0.0), y(0.0) {}
Point::Point(float x, float y) : x(x), y(y) {}
// the radius of a point is always 0.0
float Point::radius() const
{ return 0.0; }
// compute the distance to another point
float Point::distance(const Point& p) const
{ return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)); }

You also need a container to hold the objects. Since Point is the base class of the hierarchy, you will use an STL vector of pointers to Point to store and serialize geometric objects. I've added a collisions method to compute the number of collisions among objects in the container and an empty method. Listing Five is the declaration of the Objects class with the method's source code in Listing Six.

Listing Five

// file: objects.h 
#import "point.h"  // import the point definitions
#import "stl.h"    // need STL (stl.h is part of gSOAP's package)
class Objects
{ public:
   std::vector<Point*> object;
   Objects();
   int collisions() const;
   bool empty() const;
};

Listing Six

// file: objects.cpp 
#include "soapH.h"
Objects::Objects() : object() {}
// compute the number of collisions
int Objects::collisions() const
{  int hits = 0;
   for (std::vector<Point*>::const_iterator i = object.begin(); i !=
     object.end(); ++i)
   for (std::vector<Point*>::const_iterator j = i; j != object.end();
     ++j)
      if (*i != NULL && *j != NULL && *i != *j
         && (*i)->distance(**j) <= (*i)->radius() + (*j)->radius())
            hits++;
   return hits;
}
// empty collection?
bool empty() const
{ return object.empty(); }

Objects.h imports the point.h and predefined stl.h definitions. The #import directive imports a file into the current header file. Any occurrences of #include and #define are deferred until C/C++ source code compilation after code generation with the gSOAP compiler. Listing Seven shows the service specification. The service operation cws__detect_collisions takes a collection of objects as input and sets the integer hits parameter. The return value of an operation is always int (SOAP_OK or error code). The cws__ prefix binds the C-style operation to the name and its endpoint port via the //gsoap cws service annotations.

Listing Seven

// file: service.h
#import "objects.h"  // import the object collection
// define a name for the 'cws' service
//gsoap cws service name: CollisionService
// define the endpoint location of the service
//gsoap cws service port: http://localhost:8080
// define the 'cws' service operation
int cws__detect_collisions(Objects objects, int& hits);

Listing Eight is the complete source code for the implementation of the service. The service runs as a standalone sequential web service accepting SOAP requests at port 8080. The soap_serve function is generated by gSOAP. It parses the SOAP/XML request and dispatches the request to the service operation cws__detect_collisions. The service can be multithreaded and incorporate SSL encryption and/or Zlib compression by adding a few lines of code. (The details are available in the gSOAP documentation. Or, you can use the CGI, Fast-CGI, Apache mod, and IIS interfaces.) gSOAP supports document/literal style by default. If you're not familiar with the differences in encoding styles, don't worry because gSOAP generates a WSDL CollisionService.wsdl from this specification with the necessary details.

Listing Eight

// file: service.cpp
#include "soapH.h"
#include "CollisionService.nsmap"
main()
{  // set up a gSOAP runtime context
   struct soap *soap = soap_new();
   // bind to port 8080 (service endpoint)
   soap_bind(soap, NULL, 8080, 100);
   // server loop
   for (;;)
   {  // accept request
      soap_accept(soap);
      if (soap->error != SOAP_OK)
      {  soap_print_fault(soap, stderr);
         break;
      }
      // dispatch the client request
      if (soap_serve(soap) != SOAP_OK)
         soap_print_fault(soap, stderr);
     // remove class instances
     soap_destroy(soap);
     // clean up
     soap_end(soap);
   }
   // detach gSOAP context
   soap_done(soap);
   // dealloc gSOAP context
   free(soap);
}
// service operation
int cws__detect_collisions(struct soap *soap, Objects objects, int& hits)
{  if (objects.empty())
   { char *msg = soap_malloc(soap, 256);
   sprintf(msg, "Empty collection from IP=%d.%d.%d.%d", 
      (int)(soap->ip>>24)&0xFF, (int)(soap->ip>>16)&0xFF, 
        (int)(soap->ip>>8)&0xFF, (int)soap->ip&0xFF);
     return soap_sender_fault(soap, msg, NULL);
   }
   hits = objects.collisions();
   return SOAP_OK;
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.