The gSOAP compiler produces soapClientLib.cpp and soapServerLib.cpp codes that are specifically intended for building static or dynamic client/server libraries. These codes export the stubs and skeletons, but keep all marshaling code (i.e. parameter serializers and deserializers) local (i.e. as static functions) to avoid link symbol conflicts when combining multiple clients and/or servers into one executable.
To build multiple libraries in the same project directory, you can define a C++ code namespace in your header file (see Section 17.31) or you can use soapcpp2with option-pto rename the generated
soapClientLib.cpp and soapServerLib.cpp (and associated) files. The -p option specifies the file name prefix to replace thesoapprefix. The libraries don’t have to be C++ codes. You can use option-cto generate C code. A clean separation of libraries can also be achieved with C++ code namespaces, see Section 17.31.
The library codes do not define SOAP Header and Fault serializers. You MUST add SOAP Header and Fault serializers to your application, which are compiled separately as follows. First, create a new header file env.h with the SOAP Header and Fault definitions. You can leave this header file empty if you want to use the default SOAP Header and Fault. Then compile this header file with:
soapcpp2 -penv env.h
The generated envC.cpp file holds the SOAP Header and Fault serializers and you can create a (dynamic) library for it to link this code with your client or server application.
You MUST compile thestdsoap2.cpp library using-DWITH NONAMESPACES:
g++ -DWITH NONAMESPACES -c stdsoap2.cpp
This omits the reference to the global namespaces table, which is nowhere defined. You MUST explicitly set the namespaces value of the gSOAP environment in your code every time after ini- tialization of the soap struct:
soap init(&soap);
soap set namespaces(&soap, namespaces);
where the namespaces[]table is defined in the client/server source, e.g. by including the generated
.nsmapfiles. It is important to rename the tables using option-n, see Section 9.1, to avoid namespace table name clashes.
Note: Link conflicts may still occur in the unlikely situation that identical remote method names are defined in two or more client stubs or server skeletons when these methods share the same XML namespace prefix. You may have to use C++ code namespaces to avoid these link conflicts or rename the namespace prefixes used by the remote method defined in the header files.
17.32.1 C++ Example
As an example we will build a Delayed Stock Quote client library and a Currency Exchange Rate client library.
First, we create an empty header file env.h (which may contain optional SOAP Header and Fault definitions), and compile it as follows:
soapcpp2 -penv env.h g++ -c envC.cpp
We also compilestdsoap2.cppwithout namespaces:
g++ -c -DWITH NONAMESPACES stdsoap2.cpp
Note: when you forget to use-DWITH NONAMESPACESyou will get an unresolved link error for the globalnamespaces table. You can define a dummy table to avoid having to recompilestdsoap2.cpp. Second, we create the Delayed Stock Quote header file specification, which may be obtained using the WSDL importer. If you want to use C++ namespaces then you need to manually add the
namespace declaration to the generated header file:
namespace quote{
//gsoap ns service name: Service //gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service location: http://services.xmethods.net/soap //gsoap ns schema namespace: urn:xmethods-delayed-quotes //gsoap ns service method-action: getQuote ””
int ns getQuote(char *symbol, float &Result); }
We then compile it as a library and we use option -nto rename the namespace table to avoid link conflicts later:
soapcpp2 -n quote.h g++ -c quoteClientLib.cpp
If you don’t want to use a C++ code namespace, you should compilequote.h“as is” with soapcpp2 option -pquote:
soapcpp2 -n -pquote quote.h g++ -c quoteClientLib.cpp
Third, we create the Currency Exchange Rate header file specification:
namespace rate{
//gsoap ns service name: Service //gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:xmethods-CurrencyExchange //gsoap ns service method-action: getRate ””
int ns getRate(char *country1, char *country2, float &Result); }
Similar to the Quote example above, we compile it as a library and we use option-nto rename the namespace table to avoid link conflicts:
soapcpp2 -n rate.h
g++ -c rateServerProxy.cpp
Fourth, we consider linking the libraries to the main program. The main program can import the
quoteServiceProxy.h and rateServiceProxy.h files to obtain client proxies to invoke the services. The proxy implementations are defined in thequoteServiceProxy.cppand rateServiceProxy.cppfiles compiled above. The-n option also affects the generation of thequoteServiceProxy.cpp and rateServiceProxy.cpp
C++ proxy codes to ensure that the gSOAP environment is properly initialized with the appropriate namespace table (so you don’t have to initialize explicitly – this feature is only available with C++ proxy and server object classes).
#include ”quoteServiceProxy.h” // get quote Service proxy #include ”rateServiceProxy.h” // get rate Service proxy #include ”quote.nsmap” // get quote namespace bindings #include ”rate.nsmap” // get rate namespace bindings int main(int argc, char *argv[])
{
if (argc <= 1) {
std::cerr << ”Usage: main ticker [currency]” << std::endl exit(0);
}
quote::Service quote; float q;
if (quote.getQuote(argv[1], q)) // get quote soap print fault(quote.soap, stderr); else { if (argc > 2) { rate::Service rate; float r;
if (rate.getRate(”us”, argv[2], r)) // get rate in US dollars soap print fault(rate.soap, stderr);
else
q *= r; // convert the quote }
std::cout << argv[1] << ”: ” << q << std::endl; }
return 0; }
To compile and link a server object is very similar. For example, assume that we need to implement a calculator service and we want to create a library for it.
namespace calc{
//gsoap ns service name: Service //gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service location: http://www.cs.fsu.edu/ engelen/calc.cgi //gsoap ns schema namespace: urn:calc
int ns add(double a, double b, double &result); int ns sub(double a, double b, double &result); int ns mul(double a, double b, double &result); int ns div(double a, double b, double &result); }
We compile this with:
soapcpp2 -n calc.h
g++ -c calcServiceObject.cpp
The effect of the-noption is that it creates local namespace tables, and a modifiedcalcServiceObject.h
server class definitions that properly initialize the gSOAP run time with the table.
#include ”calcServiceObject.h” // get Service object #include ”calc.nsmap” // get calc namespace bindings ...
calc::Service calc;
calc.serve(); // calls request dispatcher to invoke one of the functions below ...
int calc::Service::add(double a, double b, double &result); { result = a + b; returnSOAP OK; }
int calc::Service::sub(double a, double b, double &result); { result = a - b; returnSOAP OK; }
int calc::Service::mul(double a, double b, double &result); { result = a * b; returnSOAP OK; }
int calc::Service::div(double a, double b, double &result); { result = a / b; returnSOAP OK; }
In fact, thecalc::Serviceclass is derived from the struct soap. So the environment is available asthis, which can be passed to all gSOAP functions that require a soap struct context.
17.32.2 C Example
This is the same example as above, but the clients are build with pure C.
First, we create an empty header file (which may contain optional SOAP Header and Fault defini- tions), and compile it as follows:
soapcpp2 -c -penv env.h gcc -c envC.c
We also compilestdsoap2.c without namespaces:
gcc -c -DWITH NONAMESPACES stdsoap2.c
Second, we create the Delayed Stock Quote header file specification, which may be obtained using the WSDL importer.
//gsoap ns service name: Service //gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service location: http://services.xmethods.net/soap //gsoap ns schema namespace: urn:xmethods-delayed-quotes //gsoap ns service method-action: getQuote ””
int ns getQuote(char *symbol, float *Result);
We compile it as a library and we use options -n and -p to rename the namespace table to avoid link conflicts:
soapcpp2 -c -n -pquote quote.h gcc -c quoteClientLib.c
Third, we create the Currency Exchange Rate header file specification:
//gsoap ns service name: Service //gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service location: http://services.xmethods.net/soap //gsoap ns schema namespace: urn:xmethods-CurrencyExchange //gsoap ns service method-action: getRate ””
int ns getRate(char *country1, char *country2, float *Result);
We compile it as a library and we use options -n and -p to rename the namespace table to avoid link conflicts:
soapcpp2 -c -n -prate rate.h gcc -c rateClientLib.c
The main program is:
#include ”quoteStub.h” // get quote Service stub #include ”rateStub.h” // get rate Service stub
#include ”quote.nsmap” // get quote namespace bindings #include ”rate.nsmap” // get rate namespace bindings int main(int argc, char *argv[])
{
if (argc <= 1) {
fprintf(stderr, ”Usage: main ticker [currency]\n”); exit(0);
}
float q;
soap init(&soap);
soap set namespaces(&soap, quote namespaces);
if (soap call ns getQuote(&soap, ”http://services.xmethods.net/soap”, ””, argv[1], &q)) // get quote
soap print fault(&soap, stderr); else
{
if (argc > 2) {
soap set namespaces(&soap, rate namespaces); float r;
if (soap call ns getRate(&soap, ”http://services.xmethods.net/soap”, ””, ”us”, argv[2], &r)) // get rate in US dollars
soap print fault(&soap, stderr); else
q *= r; // convert the quote }
printf(”%s: %f\n”, argv[1], q); }
return 0; }
Compile and link this application withstdsoap2.o,envC.o,quoteClientLib.o, and rateClientLib.o. To compile and link a server library is very similar. Assuming that the server is named “calc” (as specified with options -n and -p), the application needs to include the calcStub.h file, link the
calcServerLib.ofile, and call calc serve(&soap) function at run time.