Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!sdd.hp.com!think.com!yale.edu!cmcl2!adm!lhc!lhc!warsaw From: warsaw@nlm.nih.gov (Barry A. Warsaw) Newsgroups: comp.lang.c++ Subject: Re: C++ classes for Sun RPC Message-ID: Date: 20 Jun 91 21:21:01 GMT References: <1991Jun19.192025.14683@colorado.edu> Sender: usenet@nlm.nih.gov (usenet news poster) Reply-To: warsaw@nlm.nih.gov Organization: Century Computing, Inc. Lines: 948 In-Reply-To: jeffm@noisette.colorado.edu's message of 19 Jun 91 19:20:25 GMT >>>>> "Jeff" == Jeff McWhirter writes: Jeff> We have a need for an O-O rpc mechanism in C++ for Sun RPC. Jeff> It would be nice to support blocking and non-blocking calls, Jeff> xdr routines for passing data, ...? It would also be nice Jeff> if something like this has been done so we won't have to do Jeff> it. Has anyone heard of or built such a mechanism??? Following my siggy is a shar file containing 3 classes which implement Sun's RPC/XDR. It is currently limited to TCP (someday I'll grab the TLI-RPC stuff) and doesn't do non-blocking, but I have built a real thing on top of it and it does work well. Also included are server and client example programs. Note that these classes use a string and error class which I'm not including -- you are probably using your own anyway, and if not, they're easy to write/fake/replace. Oh yeah, there are some bugs/typo's in the RPC header files that come with SunC++ 2.0. We haven't gotten 2.1 yet, but I understand that some of the bugs were fixed, others remain (especially check the prototype for xdr_u_char()). Disclaimers on all the code, blah, blah, blah. This stuff has only been compiled/tested on a Sun SS1+ running SunOS 4.1.1, compiled with SunC++ 2.0. Enjoy, -Barry P.S. Someone should really start up an archive/FAQ for this newsgroup. NAME: Barry A. Warsaw INET: warsaw@nlm.nih.gov TELE: (301) 496-1936 UUCP: uunet!nlm.nih.gov!warsaw -------------------- cut here -------------------- #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'client.hh' <<'END_OF_FILE' X// $Id: client.hh,v 3.0 91/06/19 15:21:15 warsaw Exp $ X// X// $Log: client.hh,v $ X// Revision 3.0 91/06/19 15:21:15 warsaw X// Bumping to Revision 3.0 X// X// Revision 2.0 91/06/19 15:20:23 warsaw X// Bumping to Revision 2.0 X// X// Revision 1.8 91/05/23 10:20:53 warsaw X// added default timeout to ctor X// X// Revision 1.7 91/05/22 16:44:21 warsaw X// protocol is always going to be tcp X// also, changed the class name from RPC_clnt to client X// X// Revision 1.6 91/05/21 17:28:51 warsaw X// fixed some more typos X// X// Revision 1.5 91/05/21 17:27:48 warsaw X// fixed some syntax typos X// X// Revision 1.4 91/05/20 15:02:57 warsaw X// check for sizeof_outdata > 0 X// X// Revision 1.3 91/05/20 12:46:51 warsaw X// minor #endif fix X// X// Revision 1.2 91/05/20 12:41:34 warsaw X// simplified and moved inline X// X// Revision 1.1 91/05/16 17:16:11 warsaw X// Initial revision X// X X// This class represents an RPC communications client. It handles X// opening the connection with the server. It also handles remote X// execution by the caller of RPC remote procedures, along with X// argument, return values and xdr conversion routines. by default tcp X// protocol is used. X X X#ifndef client_hh X#define client_hh X X#include // for bzero() X#include // rpc.h needs this X#include // for rpc code X#include "string.hh" X#include "error.hh" X X Xstatic timeval _DEFAULT_CLIENT_TIMEOUT = { 25, 0 }; X X X// ====================================================================== X// class definition X Xclass client X{ Xpublic: X inline client( string server, X u_long prognum, u_long versnum, X timeval timeout = _DEFAULT_CLIENT_TIMEOUT ); X inline ~client( void ); X X // execute remote procedure on server, returns zero on X // success, non-zero on failure. X inline int rexec( u_long procedure, X xdrproc_t in_converter, char*& in, X xdrproc_t out_converter, char*& out, X int sizeof_outdata ); X X // get and set the timeout value X inline void timeout( timeval& new_timeout ); X inline timeval timeout( void ); X Xprivate: X CLIENT* cptr; // RPC client struct X u_long pnum; X u_long vnum; X timeval to_val; X}; X X X// ====================================================================== X// ctor and dtor X Xinline client::client( string server, X u_long prognum, u_long versnum, X timeval timeout ) X : cptr( 0 ), X pnum( prognum ), X vnum( versnum ), X to_val( timeout ) X{ X error err( "client(RPC)" ); X X cptr = clnt_create( server, prognum, versnum, "tcp" ); X if( cptr == NULL ) { X clnt_pcreateerror( server ); X err.fatal( "connection not established.\n" ); X } X}; X X Xinline client::~client( void ) X{ X clnt_destroy( cptr ); X}; X X X// ====================================================================== X// remote execution X Xinline int client::rexec( u_long procedure, X xdrproc_t in_converter, char*& in, X xdrproc_t out_converter, char*& out, X int sizeof_outdata ) X{ X error err( "client::rexec()" ); X X if( out && sizeof_outdata > 0 ) X bzero( out, sizeof_outdata ); X X return( clnt_call( cptr, procedure, X in_converter, in, X out_converter, out, X to_val ) != RPC_SUCCESS ); X}; X X X// ====================================================================== X// get/set the timeout X Xinline void client::timeout( timeval& new_timeout ) X{ X to_val = new_timeout; X}; X X Xinline timeval client::timeout( void ) X{ X return( to_val ); X}; X X X#endif client_hh END_OF_FILE if test 3462 -ne `wc -c <'client.hh'`; then echo shar: \"'client.hh'\" unpacked with wrong size! fi # end of 'client.hh' fi if test -f 'server.hh' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'server.hh'\" else echo shar: Extracting \"'server.hh'\" \(4285 characters\) sed "s/^X//" >'server.hh' <<'END_OF_FILE' X// $Id: server.hh,v 3.0 91/06/19 15:21:10 warsaw Exp $ X// X// $Log: server.hh,v $ X// Revision 3.0 91/06/19 15:21:10 warsaw X// Bumping to Revision 3.0 X// X// Revision 2.0 91/06/19 15:20:20 warsaw X// Bumping to Revision 2.0 X// X// Revision 1.2 91/05/23 11:26:51 warsaw X// had to check callback mechanism, no real changes X// X// Revision 1.1 91/05/23 09:52:26 warsaw X// Initial revision X// X X X// this class is the server analog for the client class. it implements X// the dispatching of RPC commands. it handles registration of the X// program with the portmapper for automatic port number assignments. X// it also handles registration of callback functions as calls to X// this class' (or derived class) member functions. X X X#ifndef server_hh X#define server_hh X X#include // rpc.h needs this X#include // for RPC stuff X#include "error.hh" X X X// ====================================================================== X// this typedef defines the form of a callback function. the callback X// function must be a member of server class or a member of a class X// derived from server. `in' points to an pre-allocated area of data X// which will contain the arguments from the remote caller. `out' X// points to pre-allocated data in which the callback function will X// place its results. the callback function must know how to cast in X// and out appropriately. also, the callback function SHOULD NOT X// de-allocate the memory pointed to by in and out. X Xclass server; Xtypedef void (server::*rpc_handler)( char*& in, char*& out ); X X X// ====================================================================== X// class definition X Xclass server X{ Xpublic: X // it is an error to instantiate more than 1 instance of the X // server class. this is because RPC can only handle a single X // callback on the socket. Note that there is no destructor X // for this class since (due to the nature of the run method), X // it will never be destroyed until the entire process dies. X server( u_long prognum, u_long versnum ); X X // register an RPC procedure with the appropriate callback X // function which can handle this procedure call. X void regproc( u_long procedure, rpc_handler callback, X xdrproc_t in_converter, int indata_size, X xdrproc_t out_converter, int outdata_size ); X X // once all callbacks are registered, the application can call X // the run method below to start up the RPC server. it is an X // error for run() to ever return so if a callback is used to X // terminate the server, it should call exit() X inline void run( void ); X X // returns non-zero if the application was started up by the X // inet daemon X inline int inetd_p( void ); X Xprotected: X // this function actually does the dispatching to the member X // function that can handle the remote procedure call. it is X // not directly called by the RPC level code. X virtual void dispatch( svc_req* request, SVCXPRT* transp ); X Xprivate: X // this stuff is used to interface to the RPC callback X // mechanisms. it simply finds the instance object and calls X // the dispatch() method. X static server* instance; X static inline void rpc_callback( svc_req* request, SVCXPRT* transp ); X X SVCXPRT* transp; // RPC server handle X int StartedByINETD_p; // started by inetd? X X // callback process table entries X struct ProcTableEntry { X u_long procnum; X xdrproc_t inproc; X xdrproc_t outproc; X int insz; X int outsz; X rpc_handler callback; X ProcTableEntry* next; X } *head; X}; X X X// ====================================================================== X// inline methods X Xinline void server::run( void ) X{ X error err( "server::run()" ); X svc_run(); X err.fatal( "this should never return!" ); X}; X X Xinline int server::inetd_p( void ) X{ X return( StartedByINETD_p ); X}; X X X// ====================================================================== X// this is the routine which is registered with RPC X Xvoid server::rpc_callback( svc_req* request, SVCXPRT* transp ) X{ X // this is necessary since this function will not be called X // with a valid `this' pointer. it will be called by X // svc_run() so we need to get the object on which to dispatch X // member callback functions. X server* whoami = server::instance; X whoami->dispatch( request, transp ); X}; X X X#endif server_hh END_OF_FILE if test 4285 -ne `wc -c <'server.hh'`; then echo shar: \"'server.hh'\" unpacked with wrong size! fi # end of 'server.hh' fi if test -f 'xdr.hh' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xdr.hh'\" else echo shar: Extracting \"'xdr.hh'\" \(1784 characters\) sed "s/^X//" >'xdr.hh' <<'END_OF_FILE' X// $Id: xdr.hh,v 3.0 91/06/19 15:21:14 warsaw Exp $ X// X// $Log: xdr.hh,v $ X// Revision 3.0 91/06/19 15:21:14 warsaw X// Bumping to Revision 3.0 X// X// Revision 2.0 91/06/19 15:20:23 warsaw X// Bumping to Revision 2.0 X// X// Revision 1.3 91/06/10 13:55:36 warsaw X// added Xchar and Xuchar X// X// Revision 1.2 91/05/28 10:05:02 warsaw X// fixed static initialization of static variables X// we no longer use a static class object as this wasn't working for some X// (unknown) reason. X// X// Revision 1.1 91/05/24 15:57:13 warsaw X// Initial revision X// X X X// this class contains routines to serialize and deserialize XDR data X// streams. it is most useful in conjunction with the RPC server and X// client classes. X X X#ifndef xdr_hh X#define xdr_hh X X#include // for rpc.h X#include // for xdr stuff X#include // for string class X X X// ====================================================================== X// structures that are useful X Xstruct xdrOpaque { X u_int length; // length of opaque data X char* handle; // handle X}; X X X X// ====================================================================== X// class definition X Xclass xdr X{ Xpublic: X static xdrproc_t Xchar; X static xdrproc_t Xshort; X static xdrproc_t Xint; X static xdrproc_t Xlong; X static xdrproc_t Xuchar; X static xdrproc_t Xushort; X static xdrproc_t Xuint; X static xdrproc_t Xulong; X static xdrproc_t Xfloat; X static xdrproc_t Xdouble; X X static xdrproc_t Xenum_t; X static xdrproc_t Xbool_t; X static xdrproc_t Xvoid; X X static xdrproc_t Xwrapstring; X static xdrproc_t Xstringclass; X static xdrproc_t Xopaque; X Xprotected: X static bool_t xdr_stringclass( XDR* xdrs, string* obj ); X static bool_t xdr_Opaque( XDR* xdrs, xdrOpaque* obj ); X}; X X#endif xdr_hh END_OF_FILE if test 1784 -ne `wc -c <'xdr.hh'`; then echo shar: \"'xdr.hh'\" unpacked with wrong size! fi # end of 'xdr.hh' fi if test -f 'client_example.cc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'client_example.cc'\" else echo shar: Extracting \"'client_example.cc'\" \(1318 characters\) sed "s/^X//" >'client_example.cc' <<'END_OF_FILE' X#include X#include X#include X#include X#include X#include X#include X Xvoid usage( string progname ) X{ X cerr << "Usage: " << progname << " -h " << endl; X exit( 1 ); X}; X X Xint main( int argc, char** argv ) X{ X error err( "client_example" ); X string host( null ); X X for( int argci=1; argci" << flush; X cin >> answer; X cout << endl; X switch( answer ) { X case 1: X if( clnt.rexec( 1, X xdr::Xvoid, 0, X xdr::Xuchar, (char*)&value, X sizeof(u_char) )) X err.fatal( "connection error" ); X X cout << "client::main:" << value << endl; X break; X case 2: X char* msg = strdup( "client says, \"die\"" ); X (void)clnt.rexec( 2, X xdr::Xwrapstring, (char*)&msg, X xdr::Xvoid, 0, X 1 ); X exit( 0 ); X break; X } X } X}; END_OF_FILE if test 1318 -ne `wc -c <'client_example.cc'`; then echo shar: \"'client_example.cc'\" unpacked with wrong size! fi # end of 'client_example.cc' fi if test -f 'server.cc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'server.cc'\" else echo shar: Extracting \"'server.cc'\" \(5561 characters\) sed "s/^X//" >'server.cc' <<'END_OF_FILE' X// $Id: server.cc,v 3.0 91/06/19 15:21:06 warsaw Exp $ X// X// $Log: server.cc,v $ X// Revision 3.0 91/06/19 15:21:06 warsaw X// Bumping to Revision 3.0 X// X// Revision 2.0 91/06/19 15:20:14 warsaw X// Bumping to Revision 2.0 X// X// Revision 1.3 91/06/18 13:03:16 warsaw X// need to #include libc.h since error.hh no longer includes it X// X// Revision 1.2 91/05/23 11:26:07 warsaw X// in svc_register, I was registering the wrong callback function X// (server::dispatch) which was crashing process. MUST register X// server::rpc_callback instead (and this MUST be a static member function) X// X// Revision 1.1 91/05/23 09:52:34 warsaw X// Initial revision X// X Xstatic const char Xrcsid[]="$Id: server.cc,v 3.0 91/06/19 15:21:06 warsaw Exp $"; X X#include "server.hh" X#include // for ostrstream class X#include // for pmap_unset() X#include // sys/sockets.h needs this X#include // for socket stuff X#include // for bzero X X X// ====================================================================== X// ctor and dtor X Xserver::server( u_long prognum, u_long versnum ) X : transp( 0 ), X StartedByINETD_p( 0 ), X head( 0 ) X{ X error err( "rpc server:" ); X if( instance ) X err.fatal( "already instantiated." ); X X instance = this; X X // check to see if inetd started up the server process. if so, X // it would pass the socket on file descriptor 0. X sockaddr_in saddr; X int sz = sizeof( saddr ); // for getsockname() X int sock=0; // socket number X int proto=IPPROTO_TCP; // protocol (always TPC) X int fdtype=0; // file descriptor type X X if( !getsockname( sock, (sockaddr*)&saddr, &sz )) { X X // we must have been started up by inetd X if( saddr.sin_family != AF_INET ) X err.fatal( "not internet address family" ); X X // get socket type X sz = sizeof( int ); X if( getsockopt( sock, SOL_SOCKET, SO_TYPE, (char*)&fdtype, &sz )) { X perror( "rpc server: " ); X err.fatal( "couldn't get socket type." ); X } X StartedByINETD_p = 1; X X } else { X // we must have been started up manually, so we close any X // previous portmap registrations that may be hanging X // around (probably due to the untimely death of this X // server process). X sock = RPC_ANYSOCK; X (void) pmap_unset( prognum, versnum ); X } X X // currently, this stuff is hard-coded to work only with tcp X // transport protocol. I suppose we could eventually use udp X // also, but for now we need the extra reliance and message X // lengths > 8k bytes. X if( !fdtype || fdtype == SOCK_STREAM ) { X if( (transp=svctcp_create( sock, 0, 0 )) == NULL ) X err.fatal( "cannot create tcp service." ); X X if( !svc_register( transp, prognum, versnum, X (void (*)(...))server::rpc_callback, X proto )) { X ostrstream oss; X oss << "unable to register program " << prognum X << ", version " << versnum << char(0); X err.fatal( oss.str() ); X } X } else X err.fatal( "could not create a transport handle." ); X}; X X X// ====================================================================== X// register a callback procedure X Xvoid server::regproc( u_long procedure, rpc_handler callback, X xdrproc_t in_converter, int indata_size, X xdrproc_t out_converter, int outdata_size ) X{ X error err( "server::regproc()" ); X X // first see if the procedure number has already been X // registered. if so, just change it proctable entries X for( ProcTableEntry* q=head; q && q->procnum != procedure; q=q->next ); X if( q ) { X q->inproc = in_converter; X q->outproc = out_converter; X q->insz = indata_size; X q->outsz = outdata_size; X q->callback = callback; X return; X } X X ProcTableEntry* p = new ProcTableEntry; X err.memory( p ); X X p->procnum = procedure; X p->inproc = in_converter; X p->outproc = out_converter; X p->insz = indata_size; X p->outsz = outdata_size; X p->callback = callback; X p->next = 0; X X // is this the first proc entry in the table? X if( !head ) { X head = p; X return; X } X X // add this procedure to the end of the list X for( q=head; q->next; q=q->next ); X q->next = p; X}; X X X// ====================================================================== X// dispatch to a callback X Xvoid server::dispatch( svc_req* request, SVCXPRT* transp ) X{ X error err( "server::dispatch()" ); X const u_long rexec = request->rq_proc; X X // for NULLPROC, just send ack X if( rexec == NULLPROC ) { X (void) svc_sendreply( transp, (xdrproc_t)xdr_void, (char*)NULL ); X return; X } X X if( !head ) X err.fatal( "no RPC server procedures registered." ); X X // find the proc table entry for the requested procedure X for( ProcTableEntry* q=head; q && q->procnum != rexec; q=q->next ); X if( !q ) { X ostrstream oss; X oss << "remote procedure " << rexec X << " is not registered with server dispatcher." << char(0); X svcerr_noproc( transp ); X err.nonfatal( oss.str() ); X return; X } X X char* in = new char [q->insz]; X err.memory( in ); X bzero( in, q->insz ); X X char* out = new char [q->outsz]; X err.memory( out ); X bzero( out, q->outsz ); X X // get arguments X if( !svc_getargs( transp, q->inproc, in )) { X svcerr_decode( transp ); X delete in; X delete out; X err.nonfatal( "couldn't get arguments." ); X return; X } X X // dispatch to callback function X (this->*(q->callback))( in, out ); X X // send reply to client and free XDR data X if( !svc_sendreply( transp, q->outproc, out )) { X svcerr_systemerr( transp ); X err.nonfatal( "couldn't send reply." ); X } X if( !svc_freeargs( transp, q->inproc, in )) X err.fatal( "unable to free XDR arguments." ); X X delete in; X delete out; X}; END_OF_FILE if test 5561 -ne `wc -c <'server.cc'`; then echo shar: \"'server.cc'\" unpacked with wrong size! fi # end of 'server.cc' fi if test -f 'server_example.cc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'server_example.cc'\" else echo shar: Extracting \"'server_example.cc'\" \(807 characters\) sed "s/^X//" >'server_example.cc' <<'END_OF_FILE' X#include X#include X#include X X Xclass svc : public server X{ Xpublic: X svc( int pnum, int vnum ) : server( pnum, vnum ) {}; X X void incr( char*&, char*& out ); X void die( char*&, char*& ); X}; X X Xvoid svc::incr( char*&, char*& out ) X{ X static u_char i; X X u_char* rtn = (u_char*)out; X *rtn = ++i; X cout << "svc::incr: " << *rtn << endl; X}; X X Xvoid svc::die( char*& in, char*& ) X{ X char** arg = (char**)in; X cout << "server exiting with client message: " << *arg << endl; X exit( 0 ); X}; X X Xint main( int, char** ) X{ X svc svcobj( 1, 1 ); X X svcobj.regproc( 1, (rpc_handler)&svc::incr, X xdr::Xvoid, 1, X xdr::Xuchar, sizeof(u_char) ); X X svcobj.regproc( 2, (rpc_handler)&svc::die, X xdr::Xwrapstring, sizeof(char*), X xdr::Xvoid, 1 ); X X svcobj.run(); X exit( 1 ); X}; END_OF_FILE if test 807 -ne `wc -c <'server_example.cc'`; then echo shar: \"'server_example.cc'\" unpacked with wrong size! fi # end of 'server_example.cc' fi if test -f 'xdr.cc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xdr.cc'\" else echo shar: Extracting \"'xdr.cc'\" \(4042 characters\) sed "s/^X//" >'xdr.cc' <<'END_OF_FILE' X// $Id: xdr.cc,v 3.0 91/06/19 15:21:09 warsaw Exp $ X// X// $Log: xdr.cc,v $ X// Revision 3.0 91/06/19 15:21:09 warsaw X// Bumping to Revision 3.0 X// X// Revision 2.0 91/06/19 15:20:19 warsaw X// Bumping to Revision 2.0 X// X// Revision 1.7 91/06/10 13:55:20 warsaw X// added Xchar and Xuchar X// X// Revision 1.6 91/06/05 17:09:33 warsaw X// optimized xdr_stringclass when string object is null. X// X// Revision 1.5 91/06/05 16:47:12 warsaw X// fixed xdr_stringclass to correctly handle "null" string X// objects. cannot just pass a (char*)0 to xdr_string so we first send a X// u_char indicating nullness of following string. X// X// Revision 1.4 91/06/04 12:09:10 warsaw X// fixed xdr_stringclass so that it correctly handles non-null X// terminated strings by first (de)serializing the length, then the array X// of bytes. still have to handle (EN/DE)CODE and FREE separately. X// X// Revision 1.3 91/05/28 10:06:51 warsaw X// fixed static initialization of static variables X// X// Revision 1.2 91/05/24 16:26:56 warsaw X// forgot to set initP after initial check X// X// Revision 1.1 91/05/24 15:57:18 warsaw X// Initial revision X// X Xstatic const char Xrcsid[] = "$Id: xdr.cc,v 3.0 91/06/19 15:21:09 warsaw Exp $"; X X#include "xdr.hh" X#include X X X// ====================================================================== X// static variable initializations X Xxdrproc_t xdr::Xchar = (xdrproc_t) xdr_char; Xxdrproc_t xdr::Xshort = (xdrproc_t) xdr_short; Xxdrproc_t xdr::Xint = (xdrproc_t) xdr_int; Xxdrproc_t xdr::Xlong = (xdrproc_t) xdr_long; Xxdrproc_t xdr::Xuchar = (xdrproc_t) xdr_u_char; Xxdrproc_t xdr::Xushort = (xdrproc_t) xdr_u_short; Xxdrproc_t xdr::Xuint = (xdrproc_t) xdr_u_int; Xxdrproc_t xdr::Xulong = (xdrproc_t) xdr_u_long; Xxdrproc_t xdr::Xfloat = (xdrproc_t) xdr_float; Xxdrproc_t xdr::Xdouble = (xdrproc_t) xdr_double; X Xxdrproc_t xdr::Xenum_t = (xdrproc_t) xdr_enum; Xxdrproc_t xdr::Xbool_t = (xdrproc_t) xdr_bool; Xxdrproc_t xdr::Xvoid = (xdrproc_t) xdr_void; X Xxdrproc_t xdr::Xwrapstring = (xdrproc_t) xdr_wrapstring; Xxdrproc_t xdr::Xstringclass = (xdrproc_t) xdr::xdr_stringclass; Xxdrproc_t xdr::Xopaque = (xdrproc_t) xdr::xdr_Opaque; X X X// ====================================================================== X// non standard XDR functions X Xbool_t xdr::xdr_stringclass( XDR* xdrs, string* obj ) X{ X // we need these because we must pass address to the X // underlying xdr routines. we cannot directly access the data X // members of class string and we cannot get an address of a X // non-lvalue (which member functions return). X static char* data; X static u_int size; X X // this u_char is a null indicator. if data==(char*)0 or X // size==0, then obj is the "null" string and we must do some X // special case serializing/deserializing X static u_char null_p; X X switch( xdrs->x_op ) { X case XDR_ENCODE: X null_p = (obj->null_p()) ? 1 : 0; X X if( !xdr_u_char( xdrs, &null_p )) X return( FALSE ); X X // if the object is null, just send the null indicator X if( null_p ) break; X X data = (char*)(*obj); X size = (u_int)obj->size(); X X if( !xdr_u_int( xdrs, &size )) X return( FALSE ); X X if( !xdr_string( xdrs, &data, size )) X return( FALSE ); X X break; X case XDR_DECODE: X data = 0; X X if( !xdr_u_char( xdrs, &null_p )) X return( FALSE ); X X // if the null indicator is non-zero, then nothing else is X // being sent. X if( null_p ) { X (*obj) = null; X break; X } X X if( !xdr_u_int( xdrs, &size )) X return( FALSE ); X X if( !xdr_string( xdrs, &data, size )) X return( FALSE ); X X (*obj) = string( data, size ); X break; X case XDR_FREE: X if( !xdr_u_char( xdrs, &null_p )) X return( FALSE ); X X if( null_p ) break; X X if( !xdr_u_int( xdrs, &size )) X return( FALSE ); X X if( !xdr_string( xdrs, &data, size )) X return( FALSE ); X X break; X } X return( TRUE ); X}; X X Xbool_t xdr::xdr_Opaque( XDR* xdrs, xdrOpaque* obj ) X{ X if( !xdr_u_int( xdrs, &obj->length )) X return( FALSE ); X if( !xdr_opaque( xdrs, (char*)&obj->handle, obj->length )) X return( FALSE ); X X return( TRUE ); X}; END_OF_FILE if test 4042 -ne `wc -c <'xdr.cc'`; then echo shar: \"'xdr.cc'\" unpacked with wrong size! fi # end of 'xdr.cc' fi echo shar: End of shell archive. exit 0