Remote Procedure Calls

Remote procedure calling (RPC) refers to the concept of executing fragments of code on a remote computer.

With ContRap it is very easy to implement RPC, since the code and the results of the remote computation can simply be transported as strings to or from the remote host. In particular, the answer of the remote host is also a string. To transport data to and from the remote host, you can use the serialization mechanisms of ContRap. Since serialization uses hexadecimal string representation of the data the complete communication protocol for RPC in ContRap can be realized exclusively within the ContRap language.

Server side

To run the interpreter of ContRap as an RPC server on a remote host call in your shell
> crp --remote 8000
where you might replace the number 8000 by a port number of your choice. For this tutorial we start the server on the local machine.
system("crp --remote 8000 &")
0

Client side

Remote procedure calls are activated in ContRap on the client side by loading the "rpc" package.
load("rpc")
["rpc","send","receive","Serializer","unserialize","int","double","String","save","read"]
After you have started your server, it responds to remote calls via the "rpc"-command
rpc("1+1")
2
The command simply takes a string with ContRap code as input and executes it on the remote host. The result is returned also as a string and executed on your local machine. All RPC commands optionally take the host and the port of the machine. See the corresponding command documentation.

Per default the "rpc"-command waits for the remote host to return the result. This can be bypassed, e.g. if the computation is involving, by using the wait-option
rpc("1+1";wait=false)
Then no response is received from the sever side.

You might have already noticed that you can receive remote data to your local memory, e.g. when receiving a result of a remote computation. You can transmit your local variables manually to the remote host by calling the send command.
send(1,"a")
The contents you send to the remote machine are stored in the variable given as the second argument.
rpc("a")
1
The whole RPC framework relies on the fact that you provide a serialization procedure for each object you want to transmit or receive. To implement serialization for your custom objects you have to write a constructor of the Serializer-object with your custom type and a constructor of your custom type from the Serializer object. The conversion is done implicitly.
Serializer
exported class {                                                                                                   
  Serializer = [function(value:int):Serializer,function(value:double):Serializer,function(value:String):Serializer]
  serialize = [function(serializer:Serializer):String]                                                             
}                                                                                                                  
int
exported class {                                                                                                 
  int = [function(value:double):int,explicit function(value:std::string):int,function(serializer:Serializer):int]
  mod = [function(lvalue:int,rvalue:int):int]                                                                    
  operator++ = [function(value:int):int]                                                                         
  operator-- = [function(value:int):int]                                                                         
}                                                                                                                
The result of serialization can be seen by calling the "serialize" method on the Serializer-object.
Serializer(1):serialize()
"int int int 8 01000000"
You can stop the remote machine by sending the "quit" command via "rpc".
rpc("quit")