File: README

package info (click to toggle)
camlrpc 0.4.1-7
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 1,080 kB
  • ctags: 1,474
  • sloc: ml: 11,901; makefile: 592; sh: 345; ansic: 331
file content (815 lines) | stat: -rw-r--r-- 32,123 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
******************************************************************************
README - Sun RPC
******************************************************************************


==============================================================================
Abstract
==============================================================================

RPC is a package supporting the Sun RPC protocol. RPC programs, procedures, 
clients, and servers can be dynamically represented and modified. Of course, 
there is also a classical RPC generator which generates functions doing the 
language mapping from XDR values to language values and vice versa.

==============================================================================
Download
==============================================================================

You can download RPC as gzip'ed tarball [1]. 

==============================================================================
Documentation
==============================================================================

Sorry, there is no manual. The mli files describe each function in detail. 
Furthermore, the following additional information may be useful.

------------------------------------------------------------------------------
Introduction to ocamlrpcgen
------------------------------------------------------------------------------

The tool ocamlrpcgen generates O'Caml modules which greatly simplify the 
creation and invocation of remote procedures. For example, if we have an XDR 
definition calculate.x 

program P {
  version V {
    int add(int,int) = 1;
  } = 2;
} = 3;

the generation of a corresponding RPC client is done by 

ocamlrpcgen -aux -clnt calculate.x

and the tool will generate an RPC server by 

ocamlrpcgen -aux -srv calculate.x

The flag -aux causes ocamlrpcgen to create a module Calculate_aux containing 
types, and constants from the XDR definition, and containing conversion 
functions doing the language mapping from XDR to O'Caml and vice versa. 

Calculate_aux defines the types for the arguments of the procedure and the 
result as follows: 

type t_P'V'add'arg =                      (* Arguments *)
      ( Rtypes.int4 * Rtypes.int4 )
and t_P'V'add'res =                       (* Result *)
      Rtypes.int4

Note that the XDR integer type is mapped to Rtypes.int4 which is an opaque type 
representing 4-byte signed integers. Rtypes defines conversion functions for 
int4 to/from other O'Caml types. If Rtypes.int4 is not what you want, you can 
select a different integer mapping on the command line of ocamlrpcgen. For 
example, -int int32 selects that you want the built-in int32 integer type, and 
-int unboxed selects that you want the built-in int integer type. Note (1) that 
you can also select the integer mapping case-by-case (see below), and (2) that 
there is a corresponding switch for the XDR hyper type (8-byte integers).

Calculate_aux defines also constants (none in our example), conversion 
functions, XDR type terms, and RPC programs. These other kinds of definitions 
can be ignored for the moment.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Generating clients with ocamlrpcgen
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

The flag -clnt causes ocamlrpcgen to generate the module Calculate_clnt 
containing functions necessary to contact a remote program as client. Here, 
Calculate_clnt has the signature: 

module P : sig
  module V : sig
    open Calculate_aux
    val create_client :
            ?esys:Unixqueue.event_system ->
            Rpc_client.connector ->
            Rpc.protocol ->
            Rpc_client.t
    val create_portmapped_client :
            ?esys:Unixqueue.event_system ->
            string ->
            Rpc.protocol ->
            Rpc_client.t
    val add : Rpc_client.t -> t_P'V'add'arg -> t_P'V'add'res
    val add'async :
            Rpc_client.t ->
            t_P'V'add'arg ->
            ((unit -> t_P'V'add'res) -> unit) ->
            unit
  end
end

Normally, the function P.V.create_portmapped_client is the preferred function 
to contact the RPC program. For example, to call the "add" procedure running on 
host "moon", the following statements suffice: 

let client = Calculator_clnt.P.V.create_portmapped_client "moon" Rpc.Tcp in
let n = Calculator_clnt.P.V.add client (m1,m2) in
Rpc_client.shut_down client;

That's all for a simple client! 

The invocation of P.V.create_portmapped_client first asks the portmapper on 
"moon" for the TCP instance of the program P.V, and stores the resulting 
internet port. Because we wanted TCP, the TCP connection is opened, too. When 
P.V.add is called, the values m1 and m2 are XDR-encoded and sent over the TCP 
connection to the remote procedure; the answer is XDR-decoded and returned, 
here n. Finally, the function Rpc_client.shut_down closes the TCP connection.

Of course, this works for UDP transports, too; simply pass Rpc.Udp instead of 
Rpc.Tcp.

The function P.V.create_client does not contact the portmapper to find out the 
internet port; you must already know the port and pass it as connector argument 
(see Rpc_client for details).

You could have also invoked "add" in an asynchronous way by using 
P.V.add'async. This function does not wait until the result of the RPC call 
arrives; it returns immediately. When the result value has been received, the 
function passed as third argument is called back, and can process the value. An 
application of asynchronous calls is to invoke two remote procedures at the 
same time: 

let esys = Unixqueue.create_event_system() in
let client1 = Calculator_clnt.P.V.create_portmapped_client 
                ~esys:esys "moon" Rpc.Tcp in
let client2 = Calculator_clnt.P.V.create_portmapped_client 
                ~esys:esys "mars" Rpc.Tcp in
let got_answer1 get_value =
  let v = get_value() in
  print_endline "moon has replied!"; ... in
let got_answer2 get_value =
  let v = get_value() in
  print_endline "mars has replied!"; ... in
Calculator_clnt.P.V.add'async client1 (m1,m2) got_answer1;
Calculator_clnt.P.V.add'async client2 (m3,m4) got_answer1;
Unixqueue.run esys

Here, the two clients can coexist because they share the same event system (see 
the Unixqueue module); this system manages it that every network event on the 
connection to "moon" will be forwarded to client1 and that the network events 
on the connection to "mars" will be forwarded to client2. The add'async calls 
do not block; they only register themselves with the event system and return 
immediately. Unixqueue.run starts the event system: The XDR-encoded values 
(m1,m2) are sent to "moon", and (m3,m4) to "mars"; replies are recorded. Once 
the reply of "moon" is complete, got_answer1 is called; once the reply of 
"mars" has been fully received, got_answer2 is called. These functions can now 
query the received values by invoking get_value; note that get_value will 
either return the value or raise an exception if something went wrong. When 
both answers have been received and processed, Unixqueue.run will return. 

Obviously, asynchronous clients are a bit more complicated than synchronous 
ones; however it is still rather simple to program them. For more information 
on how the event handling works, see the Equeue User's Guide [2].

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Generating servers with ocamlrpcgen
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

The flag -srv causes ocamlrpcgen to generate the module Calculate_srv 
containing functions which can act as RPC servers. Here, Calculate_srv has the 
signature: 

module P : sig
  module V : sig
    open Calculate_aux
    val create_server :
            ?limit:int ->
            proc_add : (t_P'V'add'arg -> t_P'V'add'res) ->
            Rpc_server.connector ->
            Rpc.protocol ->
            Rpc.mode ->
            Unixqueue.event_system ->
            Rpc_server.t
    val create_async_server :
            ?limit:int ->
            proc_add : (Rpc_server.session ->
                        t_P'V'add'arg ->
                        (t_P'V'add'res -> unit) ->
                        unit) ->
            Rpc_server.connector ->
            Rpc.protocol ->
            Rpc.mode ->
            Unixqueue.event_system ->
            Rpc_server.t
    end
end

There are two functions: P.V.create_server acts as a synchronous server, and 
P.V.create_async_server works as asynchronous server. Let's first explain the 
simpler synchronous case.

P.V.create_server accepts a number of labeled arguments and a number of 
anonymous arguments. There is always an optional ?limit parameter limiting the 
number of pending connections accepted by the server (default: 20); this is the 
second parameter of the listen system call. For every procedure p realized by 
the server there is a labeled argument proc_p passing the function actually 
computing the procedure. For synchronous servers, this function simply gets the 
argument of the procedure and must return the result of the procedure. In this 
example, we only want to realize the "add" procedure, and so there is only a 
proc_add argument. The anonymous Rpc_server.connector argument specifies the 
internet port (or the file descriptor) on which the server will listen for 
incoming connections. The Rpc.protocol argument defines whether this is a 
TCP-like (stream-oriented) or a UDP-like (datagram-oriented) service. The 
Rpc.mode parameter selects how the connector must be handled: Whether it acts 
like a socket or whether is behaves like an already existing bidirectional 
pipeline. Finally, the function expects the event system to be used as last 
argument.

For example, to define a server accepting connections on the local loopback 
interface on TCP port 6789, the following statement creates such a server: 

let esys = Unixqueue.create_event_system in
let server = 
  Calculate_srv.P.V.create_server
    ~proc_add: add
    (Rpc_server.Localhost 6789)            (* connector *)
    Rpc.Tcp                                (* protocol *)
    Rpc.Socket                             (* mode *)
    esys

Note that this statement creates the server, but actually does not serve the 
incoming connections. You need an additionally 

Unixqueue.run esys

to start the service.

Not all combinations of connectors, protocols, and modes are sensible. 
Especially the following values work:

-  TCP internet servers: One of the connectors Localhost or Portmapped; the 
   protocol Rpc.Tcp; the mode Rpc.Socket
   
-  UDP internet servers: One of the connectors Localhost or Portmapped; the 
   protocol Rpc.Udp; the mode Rpc.Socket
   
-  Stream-based Unix domain socket servers: The connector Unix, the protocol 
   Rpc.Tcp; the mode Rpc.Socket
   
-  Datagram-based Unix domain socket servers: These are not supported
   
-  Serving an already accepted (inetd) stream connection: The connector 
   Descriptor; the protocol Rpc.Tcp; the mode Rpc.BiPipe
   
-  Serving an already received (inetd) datagram: XXX (status unclear)
   
The connector Portmapped registers the service at the local portmapper, and is 
the connector of choice.

Note that servers with mode=Socket never terminate; they wait forever for 
service requests. On the contrary, servers with mode=BiPipe process only the 
current (next) request, and terminate then.

The resulting server is synchronous because the next request is only accepted 
after the previous request has been finished. This means that the calls are 
processed in a strictly serialized way (one after another); however, the 
network traffic caused by the current and by previous calls can overlap (to 
maximize network performance).

In contrast to this, an asynchronous server needs not respond immediately to an 
RPC call. Once the call has been registered, the server is free to reply 
whenever it likes to, even after other calls have been received. For example, 
you can synchronize several clients: Only after both clients A and B have 
called the procedure "sync", the replies of the procedures are sent back: 

let client_a_sync = ref None
let client_b_sync = ref None

let sync s arg send_result =
  if arg.name_of_client = "A" then
    client_a_sync := Some send_result;
  if arg.name_of_client = "B" then
    client_b_sync := Some send_result;
  if !client_a_sync <> None && !client_b_sync <> None then (
    let Some send_result_to_a = !client_a_sync in
    let Some send_result_to_b = !client_b_sync in
    send_result_to_a "Synchronized";
    send_result_to_b "Synchronized";
  )

let server =
  Sync.V.create_async_server
    ~proc_sync: sync
    ...

Here, the variables client_a_sync and client_b_sync store whether one of the 
clients have already called the "sync" service, and if so, the variables store 
also the function that needs to be called to pass the result back. For example, 
if "A" calls "sync" first, it is only recorded that there was such a call; 
because send_result is not invoked, "A" will not get a reply. However, the 
function send_result is stored in client_a_sync such that it can be invoked 
later. If "B" calls the "sync" procedure next, client_b_sync is updated, too. 
Because now both clients have called the service, synchronization has happed, 
and the answers to the procedure calls can be sent back to the clients. This is 
done by invoking the functions that have been remembered in client_a_sync and 
client_b_sync; the arguments of these functions are the return values of the 
"sync" procedure. 

It is even possible for an asynchronous server not to respond at all; for 
example to implement batching (the server receives a large number of calls on a 
TCP connection and replies only to the last call; the reply to the last call 
implicitly commits that all previous calls have been received, too).

To create multi-port servers, several servers can share the same event system; 
e.g. 

let esys = Unixqueue.create_event_system in
let tcp_server = 
  P.V.create_server ... Rpc.Tcp ... esys in
let udp_server = 
  P.V.create_server ... Rpc.Udp ... esys in
Unixqueue.run esys



------------------------------------------------------------------------------
Command line arguments of ocamlrpcgen
------------------------------------------------------------------------------

The tool accepts the following options: 

usage: ocamlrpcgen [-aux] [-clnt] [-srv]
                   [-int   (abstract | int32 | unboxed) ]
                   [-hyper (abstract | int64 | unboxed) ]  
                   [-cpp   (/path/to/cpp | none) ]
                   [-D var=value]
                   [-U var]
                   file.xdr ...



-  -aux: Creates for every XDR file the auxiliary module containing the type 
   and constant definitions as O'Caml expressions, and containing the 
   conversion functions implementing the language mapping.
   
-  -clnt: Creates for every XDR file a client module.
   
-  -srv: Creates for every XDR file a server module.
   
-  -int abstract: Uses Rtypes.int4 for signed ints and Rtypes.uint4 for 
   unsigned ints as default integer representation. This is the default. 
   
-  -int int32: Uses int32 for both signed and unsigned ints as default integer 
   representation. Note that overflows are ignored for unsigned ints; i.e. 
   large unsigned XTR integers are mapped to negative int32 values.
   
-  -int unboxed: Uses Pervasives.int for both signed and unsigned ints as 
   default integer representation. XDR values outside the range of O'Camls 31 
   bit signed ints are rejected (raise an exception).
   
-  -hyper abstract: Uses Rtypes.int8 for signed ints and Rtypes.uint8 for 
   unsigned ints as default hyper (64 bit integer) representation. This is the 
   default.
   
-  -hyper int64: Uses int64 for both signed and unsigned ints as default hyper 
   representation. Note that overflows are ignored for unsigned ints; i.e. 
   large unsigned XTR hypers are mapped to negative int64 values.
   
-  -hyper unboxed: Uses Pervasives.int for both signed and unsigned ints as 
   default hyper representation. XDR values outside the range of O'Camls 31 bit 
   signed ints are rejected (raise an exception).
   
-  -cpp /path/to/cpp: Applies the C preprocessor found under /path/to/cpp on 
   the XDR files before these are processed. The default is -cpp cpp.
   
-  -cpp none: Does not call the C preprocessor.
   
-  -D var=value: Defines the C preprocessor variable var with the given value.
   
-  -U var: Undefines the C preprocessor variable var.
   
------------------------------------------------------------------------------
The language mapping underlying ocamlrpcgen
------------------------------------------------------------------------------

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Names
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Because XDR has a different naming concept than O'Caml, sometimes identifiers 
must be renamed. For example, if you have two structs with equally named 
components 

struct a {
  t1 c;
  ...;
}

struct b {
  t2 c;
  ...;
}

the corresponding O'Caml types will be 

type a = { c : t1; ... }
type b = { c' : t2; ... }

i.e. the second occurrence of c has been renamed to c'. Note that ocamlrpcgen 
prints always a warning for such renamings that are hard to predict.

Another reason to rename an identifier is that the first letter has the wrong 
case. In O'Caml, the case of the first letter must be compatible with its 
namespace. For example, a module name must be uppercase. Because RPC programs 
are mapped to O'Caml modules, the names of RPC programs must begin with an 
uppercase letter. If this is not the case, the identifier is renamed, too.

You can specify the O'Caml name of every XDR/RPC identifier manually: Simply 
add after the definition of the identifier the phrase "=> ocaml_id" where 
ocaml_id is the preferred name for O'Caml. Example: 

struct a {
  t1 c => a_c;
  ...;
}

struct b {
  t2 c => b_c;
  ...;
}

Now the generated O'Caml types are 

type a = { a_c : t1; ... }
type b = { b_c : t2; ... }

This works wherever a name is defined in the XDR file.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Integer types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

XDR defines 32 bit and 64 bit integers, each in a signed and unsigned variant. 
As O'Caml does only know 31 bit signed integers (type "int"; the so-called 
unboxed integers), 32 bit signed integers (type "int32"), and 64 bit signed 
integers (type "int64"), it is unclear how to map the XDR integers to O'Caml 
integers. 

The module Rtypes defines the opaque types int4, uint4, int8, and uint8 which 
exactly correspond to the XDR types. These are useful to pass integer values 
through to other applications, and for simple identification of things. 
However, you cannot compute directly with the Rtypes integers. Of course, 
Rtypes also provides conversion functions to the basic O'Caml integer types 
int, int32, and int64, but it would be very inconvenient to call these 
conversions for every integer individually.

Because of this, ocamlrpcgen has the possibility to specify the O'Caml integer 
variant for every integer value (and it generates the necessary conversion 
invocations automatically). The new keywords "_abstract", "_int32", "_int64", 
and "_unboxed" select the variant to use:

-  _abstract int: A signed 32 bit integer mapped to Rtypes.int4
   
-  _int32 int: A signed 32 bit integer mapped to int32
   
-  _int64 int: A signed 32 bit integer mapped to int64
   
-  _unboxed int: A signed 32 bit integer mapped to int
   
-  unsigned _abstract int: An unsigned 32 bit integer mapped to Rtypes.uint4
   
-  unsigned _int32 int: An unsigned 32 bit integer mapped to int32 (ignoring 
   overflows)
   
-  unsigned _int64 int: An unsigned 32 bit integer mapped to int64
   
-  unsigned _unboxed int: An unsigned 32 bit integer mapped to int
   
Note that the 32 bits of the unsigned integer are simply casted to the 32 bits 
of int32 in the case of unsigned _int32 int (the meaning of the sign is 
ignored). In contrast to this, the _unboxed specifier causes a language mapping 
rejecting too small or too big values.

A similar mapping can be specified for the 64 bit integers (hypers):

-  _abstract hyper: A signed 64 bit integer mapped to Rtypes.int8
   
-  _int64 hyper: A signed 64 bit integer mapped to int64
   
-  _unboxed hyper: A signed 64 bit integer mapped to int
   
-  unsigned _abstract hyper: An unsigned 64 bit integer mapped to Rtypes.uint8
   
-  unsigned _int64 hyper: An unsigned 64 bit integer mapped to int64
   
-  unsigned _unboxed hyper: An unsigned 64 bit integer mapped to int
   
Again, unsigned _int64 hyper causes that the 64 bits of the XDR values are 
casted to int64.

If the keyword specifying the kind of language mapping is omitted, the default 
mapping applies. Unless changed on the command line (options -int and -hyper), 
the default mapping is _abstract:

-  int: means _abstract int unless changed on the command line by the -int 
   option
   
-  unsigned int: means unsigned _abstract int unless changed on the command 
   line by the -int option
   
-  hyper: means _abstract hyper unless changed on the command line by the 
   -hyper option
   
-  unsigned hyper: means unsigned _abstract hyper unless changed on the command 
   line by the -hyper option
   
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Floating-point types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

The XDR types single and double are supported and both mapped to the O'Caml 
type float. The XDR type quadruple is not supported. 

Note that the support for single is very bad; the conversion function doing the 
necessary bit shifting is written in O'Caml itself (instead of letting the CPU 
doing the dirty work). 

The code for double assumes that the CPU represents floating-point numbers 
according to the IEEE standards. 

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
String and Opaque types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Strings and opaque values are mapped to O'Caml strings. If strings have a fixed 
length or a maximum length, this constraint is checked when the conversion is 
performed. 

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Array types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Arrays are mapped to O'Caml arrays. If arrays have a fixed length or a maximum 
length, this constraint is checked when the conversion is performed. 

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Record types (struct)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Structs are mapped to O'Caml records. 

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Enumerated types (enum)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Enumerated types are mapped to Rtypes.int4 (always, regardless of what the -int 
option specifies). The enumerated constants are mapped to let-bound values of 
the same name. Example: The XDR definition 

enum e {
  A = 1;
  B = 2;
}

generates the following lines of code in the auxiliary module: 

type e = Rtypes.int4;;
val a : Rtypes.int4;;
val b : Rtypes.int4;;

However, when the XDR conversion is performed, it is checked whether values of 
enumerators are contained in the set of allowed values. 

The special enumerator bool is mapped to the O'Caml type bool.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Union types discriminated by enumerations
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Often, XDR unions are discriminated by enumerations, so this case is handled 
specially. For every case of the enumerator, a polymorphic variant is generated 
that contains the selected arm of the union. Example: 

enum e {
  A = 1;
  B = 2;
  C = 3;
  D = 4;
}

union u (e discr) {
  case A: 
    int x;
  case B:
    hyper y;
  default:
    string z;
}

This is mapped to the O'Caml type definitions: 

type e = Rtypes.int4;;
type u =
  [ `a of Rtypes.int4
  | `b of Rtypes.int8
  | `c of string
  | `d of string
  ]

Note that the identifiers of the components (discr, x, y, z) have vanished; 
they are simply not necessary in a sound typing environment. Also note that the 
default case has been "expanded"; because the cases of the enumerator are known 
it is possible to determine the missing cases meant by "default" and to define 
these cases explicitly. 

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Union types discriminated by integers
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

If the discriminant has integer type, a different mapping scheme is used. For 
every case occuring in the union definition a separate polymorphic variant is 
defined; if necessary, an extra default variant is added. Example: 

union u (int discr) {
  case -1: 
    int x;
  case 1:
    hyper y;
  default:
    string z;
}

This is mapped to the O'Caml type definition: 

type u = 
  [ `__1 of Rtypes.int4
  | `_1  of Rtypes.int8
  | `default of (Rtypes.int4 * string)
  ]

Note that positive cases get variant tags of the form "_n" and that negative 
cases get variant tags of the form "__n". The default case is mapped to the tag 
`default with two arguments: First the value of the discriminant, second the 
value of the default component.

This type of mapping is not recommended, and only provided for completeness.

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The special "option" union type
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

The XDR * type is mapped to the O'Caml option type. Example: 

typedef string *s;

is mapped to 

type s = string option



::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Recursive types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Recursive types are fully supported. Unlike in the C language, you can 
recursively refer to types defined before or after the current type definition. 
Example: 

typedef intlistbody *intlist;   /* Forward reference */
typedef struct {
  int value;
  intlist next;
} intlistbody;

This is mapped to: 

type intlist = intlistbody option
and intlistbody = 
  { value : Rtypes.int4;
    next : intlist;
  }

However, it is not checked whether there is a finite fixpoint of the recursion. 
The O'Caml compiler will do this check anyway, so it not really needed within 
ocamlrpcgen. 

------------------------------------------------------------------------------
Overview over the RPC library
------------------------------------------------------------------------------

Normally, only the following modules are of interest:

-  Rtypes: Supports serialization/deserialization of the  basic integer and fp 
   types (rtypes = remote types)
   
-  Rpc: Contains some types needed everyhwere
   
-  Rpc_client: Contains the functions supporting RPC  clients
   
-  Rpc_server: Contains the functions supporting RPC  servers
   
-  Rpc_portmapper: Functions to contact the portmapper  service
   
-  Rpc_auth_sys: AUTH_SYS style authentication.
   
------------------------------------------------------------------------------
Restrictions of the current implementation
------------------------------------------------------------------------------

The authentication styles AUTH_DH and AUTH_LOCAL are not yet supported on all 
platforms. 

The implementation uses an intermediate, symbolic representation of the values 
to transport over the network. This may restrict the performance.

Quadruple-precision fp numbers are not supported.

RPC broadcasts are not supported.

TI-RPC and rpcbind versions 3 and 4 are not supported.

------------------------------------------------------------------------------
Multi-threading
------------------------------------------------------------------------------

Currently, the library has no special support for multi-threaded applications. 
However, the library is reentrant, and this makes it possible to run several 
independent instances of the library in separated threads. Of course, this has 
only limited applications.

The library should only be used in multi-threaded environments with O'Caml >= 
3.01.

I've already written an example with a real multi-threaded server. One thread 
handles all network connections, and starts new threads when TCP connections 
are accepted. However, this example is very difficult code, and not yet ready 
for a public release. Especially, the problem is how to signal thread events 
across Unixqueue event systems.

------------------------------------------------------------------------------
RPC tunneled through HTTP
------------------------------------------------------------------------------

There is a new library part rpc-over-http that encapsulates RPC messages into 
HTTP messages. Please see the README files in src/rpc-over-http and 
examples/http_tunnel for details. 

==============================================================================
Author, Copying
==============================================================================

RPC has been written by Gerd Stolpmann [3]. You may copy it as you like, you 
may use it even for commercial purposes as long as the license conditions are 
respected, see the file LICENSE coming with the distribution. It allows almost 
everything. 

==============================================================================
History
==============================================================================

-  0.4.1: rpc now works for O'Caml 3.08alpha. Small bugfix regarding cpp on 
   MacOSX. Some improvements in Rpc_client.
   
-  0.4: As far as I remember, I did a lot of performance tuning in this 
   version. There are new "fast" versions XV_enum_fast, XV_struct_fast, 
   XV_union_over_enum_fast of already existing XDR value representations. These 
   fast versions use arrays instead of associative lists. ocamlrpcgen was 
   changed to take advantage from these improvements.
   
-  0.3.3: Fix: ocamlrpcgen outputs the right names for the server procedures in 
   mli files.
   
-  0.3.2: Several smaller fixes: unions can have "fall-through" cases (as in: 
   union ... { case A: case B: ... }). If the type "bool" is used as 
   discriminant, the generated names of the O'Caml variants are `False and 
   `True, and no longer `false and `true (which caused a syntax error in recent 
   versions of the O'Caml compiler). An error with constants > 255 has been 
   fixed, too.
   
-  0.3.1: Updated for O'Caml 3.04.
   
-  0.3: The package has now a "configure" script. The directory hierarchy has 
   been improved.
   The new library part rpc-over-http allows it to encapsulate RPC messages 
   into HTTP messages. This makes it possible to call remote services behind 
   firewalls.
   Support for the authentication methods AUTH_SYS, AUTH_DH and local 
   authentication. The latter two should be regarded as experimental, though.
   Especially the two core modules Rpc_client and Rpc_server have been 
   completely revised.
   There are now regression tests for the core functionality.
   The distribution contains a comprehensive example of RPC programming: A 
   server for file queues.
   
-  0.2.2: Fixed some bugs with big messages.
   
-  0.2: Added ocamlrpcgen. Improved the possible language mappings for integer 
   types (int32, int64).
   

--------------------------

[1]   see http://www.ocaml-programming.de/packages/rpc-0.4.1.tar.gz

[2]   
      see http://www.ocaml-programming.de/packages/documentation/equeue/users-guide/

[3]   see mailto:gerd@gerd-stolpmann.de