RInside Version 0.2.12
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
example_client.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Christian Authmann
3  */
4 
5 #include "datatypes/foo.h"
6 #include "datatypes/bar.h"
7 #include "common/constants.h"
8 #include "common/binarystream.h"
9 #include "client/rinsideclient.h"
10 
11 #include <limits>
12 #include <memory>
13 #include <sstream>
14 #include <fstream>
15 #include <string>
16 #include <mutex>
17 #include <functional>
18 #include <cmath>
19 
20 /*
21  * The following examples often talk about "serializable types". These are any
22  * user-defined objects with a TYPEID, serialize() and deserialize() methods
23  * (in this example: Foo and Bar) as well as std::string, most arithmetic types
24  * and vectors of these.
25  *
26  * See common/typeid.h for a list.
27  */
28 
29 static void test_setting_getting() {
31  RInsideClient R(stream);
32 
33  /*
34  * We can set variables in the R environment to any object we like, provided
35  * that the object is of a serializable type.
36  * We can get them accordingly.
37  */
38  Foo foo("testfoo", 42, 43);
39  printf("setting Foo(%s, %d, %d) in the R environment\n", foo.name.c_str(), foo.a, foo.b);
40  R.setValue("foo", foo);
41 
42  auto foo2 = R.getValue<Foo>("foo");
43  printf("got Foo(%s, %d, %d) via getValue\n", foo2.name.c_str(), foo2.a, foo2.b);
44 
45  auto foo3 = R.parseEval<Foo>("foo");
46  printf("got Foo(%s, %d, %d) via parseEval\n", foo3.name.c_str(), foo3.a, foo3.b);
47 
48  try {
49  auto foo = R.getValue<Foo>("IDoNotExist");
50  }
51  catch (const std::runtime_error &e) {
52  printf("Getting a nonexistent variable failed with message:\n%s\n", e.what());
53  }
54 
55  try {
56  auto bar = R.getValue<Bar>("foo");
57  }
58  catch (const std::runtime_error &e) {
59  printf("Getting foo as an object of class Bar failed with message:\n%s\n", e.what());
60  }
61 }
62 
63 
64 static void test_callbacks() {
65  // We initialize a new connection. The server will spawn a new process with a clean environment.
67  RInsideClient R(stream);
68 
69  /*
70  * We can provide C++ functions to the R environment. Parameters and return value
71  * must be of a serializable type.
72  *
73  * This has a bit of an overhead, since each time a function is called, the parameters
74  * are sent over the network from R to C++, then the function is executed, and the
75  * result is sent back from C++ to R.
76  * You will want to avoid sending large objects, and you will want to avoid calling
77  * remote functions hundreds of times per second.
78  */
79  std::function<Foo(const std::string &)> loadFoo =
80  [] (const std::string &name) -> Foo {
81  return Foo(name, name.length(), 1);
82  };
83  R.setCallback("loadFoo", loadFoo);
84 
85  std::function<Foo(const Foo &)> swapFoo =
86  [] (const Foo &foo) -> Foo {
87  return Foo(foo.name, foo.b, foo.a);
88  };
89  R.setCallback("swapFoo", swapFoo);
90 
91  std::function<Bar(int)> loadBar =
92  [] (int id) -> Bar {
93  std::string foo_name = std::string("foo_") + std::to_string(id);
94  return Bar(foo_name, Foo(foo_name, foo_name.length(), id));
95  };
96  R.setCallback("loadBar", loadBar);
97 
98  std::function<std::vector<float>(float, float, const std::vector<int> &)> calibrate =
99  [] (float offset, float scale, const std::vector<int> &in) -> std::vector<float> {
100  std::vector<float> out;
101  out.reserve(in.size());
102  for ( auto &v : in )
103  out.push_back(offset + (float) v * scale);
104  return out;
105  };
106  R.setCallback("calibrate", calibrate);
107 
108  auto foo = R.parseEval<Foo>("foo = loadFoo('loaded')");
109  printf("got Foo(%s, %d, %d) via loadFoo()\n", foo.name.c_str(), foo.a, foo.b);
110 
111  auto foo2 = R.parseEval<Foo>("swapFoo(foo)");
112  printf("got Foo(%s, %d, %d) after swapFoo()\n", foo2.name.c_str(), foo2.a, foo2.b);
113 
114  auto bar = R.parseEval<Bar>("loadBar(42)");
115  printf("got Bar(%s, Foo(%s, %d, %d))\n", bar.name.c_str(), bar.foo.name.c_str(), bar.foo.a, bar.foo.b);
116 
117  auto vec = R.parseEval<std::vector<float>>("calibrate(1.0, 0.3, c(1,2,3,4,5))");
118  printf("Got c(");
119  for (auto &v : vec)
120  printf("%.2f, ", v);
121  printf(") from calibrate()\n");
122 
123  try {
124  auto foo3 = R.parseEval<Foo>("loadFoo()");
125  printf("got Foo(%s, %d, %d) via loadFoo()\n", foo3.name.c_str(), foo3.a, foo3.b);
126  }
127  catch (const std::runtime_error &e) {
128  printf("Calling loadFoo() with wrong parameters failed with message:\n%s\n", e.what());
129  }
130  catch (...) {
131  printf("Calling loadFoo() with wrong parameters lead to an unrecoverable error, ending test\n");
132  return;
133  }
134 
135  // Passing incompatible parameters results in recoverable errors, so we can keep using the connection
136  auto x = R.parseEval<int>("x = 42;");
137  printf("Got x = %d\n", x);
138 }
139 
140 
141 static void test_console_output() {
143  RInsideClient R(stream);
144 
145  /*
146  * It's probably useful to capture the output of R's console.
147  * So here's how you do it.
148  */
149  R.parseEvalQ("print('Hello World')");
150  auto output = R.getConsoleOutput();
151  printf("Output of the R script:\n%s\n", output.c_str());
152 }
153 
154 
155 static void test_plot() {
157  RInsideClient R(stream);
158 
159  /*
160  * According to a totally representative user survey, the main use of R is to draw fancy plots [citation needed].
161  *
162  * Of course, we can do that.
163  */
164  R.initPlot(400,600);
165  R.parseEvalQ("plot(c(0,0), type = 'n', xlim=c(0,1), ylim=c(-1,1), xlab = 'x', ylab = 'y', bty='n')");
166 
167  R.parseEvalQ("lines(c(0,0), c(-1,1), col='red', add=TRUE)");
168  R.parseEvalQ("curve(-x, 0, 1, 200, col='blue', add=TRUE)");
169  R.parseEvalQ("curve(0.5+sqrt(1-x^2)/2, 0, 1, 200, col='#00FF00', add=TRUE)");
170  R.parseEvalQ("curve(0.5-sqrt(1-x^2)/2, 0, 1, 200, col='#33EE33', add=TRUE)");
171  auto png = R.getPlot();
172  printf("Got a png from the plot, saving to plot.png\n");
173 
174  std::fstream f("plot.png", std::fstream::out | std::fstream::binary | std::fstream::trunc);
175  f << png;
176  f.close();
177 }
178 
179 static void test_multiple() {
180  /*
181  * For our last trick, we'd like to show something that cannot be replicated using RInside directly:
182  * Handling multiple R environments at the same time.
183  */
185  RInsideClient R1(stream1);
186 
188  RInsideClient R2(stream2);
189 
190  R1.setValue("id", 1);
191  R2.setValue("id", 2);
192 
193  auto id1 = R1.getValue<int>("id");
194  auto id2 = R2.getValue<int>("id");
195 
196  printf("id of environment 1 is: %d, id of environment 2 is: %d\n", id1, id2);
197 }
198 
199 
200 int main(void) {
201  try {
202  printf("==========================\nTesting setting and getting:\n==========================\n");
204  printf("\n==========================\nTesting callbacks:\n==========================\n");
205  test_callbacks();
206  printf("\n==========================\nTesting console output:\n==========================\n");
208  printf("\n==========================\nTesting plots:\n==========================\n");
209  test_plot();
210  printf("\n==========================\nTesting multiple environments:\n==========================\n");
211  test_multiple();
212  }
213  catch (const BinaryStream::stream_exception &e) {
214  printf("Error communicating with the server\nDid you start ./example_server?\n");
215  }
216 }
Definition: foo.h:17
std::string name
Definition: foo.h:22
static void test_setting_getting()
void setCallback(const std::string &name, std::function< R(Params...)> &callback)
Definition: rinsideclient.h:65
static void test_multiple()
Foo swapFoo(Foo &input)
void setValue(const std::string &name, const T &value)
Definition: rinsideclient.h:38
static void test_plot()
T parseEval(const std::string &code)
Definition: rinsideclient.h:30
Definition: bar.h:19
std::string getPlot()
static void test_callbacks()
#define ris_socket_address
Definition: constants.h:8
T getValue(const std::string &name)
Definition: rinsideclient.h:49
void initPlot(uint32_t width=800, uint32_t height=600)
static void test_console_output()
int32_t a
Definition: foo.h:23
int main(void)
std::string getConsoleOutput()
static BinaryStream connectToUnixSocket(const char *)
int32_t b
Definition: foo.h:23
void parseEvalQ(const std::string &code)
Definition: rinsideclient.h:25