-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmruby-test.cpp
More file actions
279 lines (235 loc) · 8.93 KB
/
mruby-test.cpp
File metadata and controls
279 lines (235 loc) · 8.93 KB
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
// mruby-test.cpp : Defines the entry point for the console application.
// This file contains code on playing around w/ the mruby vm.
// Written by Klaus Beyer, 2015
// License MIT
// mruby version 1.1.0
//
#include "stdafx.h"
#include <ctime>
#include <iostream>
#include <sstream>
#include <vector>
#include <chrono>
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/string.h>
#include <mruby/class.h>
#include "binding.h"
#include "time_meas.h"
using namespace mruby;
// Checks the return state, if it has an ruby exception print the information.
bool check_retcode( mrb_state* mrb)
{
if( mrb->exc ) {
// Exception
mrb_value obj = mrb_funcall( mrb, mrb_obj_value( mrb->exc ), "inspect", 0 );
std::string obj_str( RSTRING_PTR( obj ), RSTRING_LEN( obj ) );
std::cout << "exception: " << obj_str << std::endl;
return false;
}
return true;
}
// Prints the object or if an exception is present its information.
void eval_retcode( mrb_state* mrb, mrb_value rc )
{
if( check_retcode(mrb) ) {
mrb_value obj = mrb_funcall( mrb, rc, "inspect", 0 );
std::string obj_str( RSTRING_PTR( obj ), RSTRING_LEN( obj ) );
std::cout << "eval: " << obj_str << std::endl;
}
}
// A simple prints string.
mrb_value mrb_t_printstr( mrb_state* mrb, mrb_value self )
{
mrb_value obj;
mrb_get_args( mrb, "o", &obj );
if( mrb_string_p( obj ) ) {
std::string obj_str( RSTRING_PTR( obj ), RSTRING_LEN( obj ) );
std::cout << "C++: " << obj_str << "\n";
}
return obj;
}
// returns a string (in an ruby object) of something like a version information.
mrb_value mrb_host_ver( mrb_state* mrb, mrb_value self )
{
std::stringstream ver;
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
ver << "Host Application 0.0.1 build on " << std::ctime(&now) << " w/ MSC ver " << _MSC_FULL_VER << " Platform x64\n";
mrb_value obj = mrb_str_new( mrb, ver.str().c_str(), ver.str().size() );
return obj;
}
double c_loadcpu( double num1, double num2 )
{
auto res = 0.0;
for( long long i = 0; i < 1000000; ++i ) {
res += num1 / num2 * (double) i;
}
return res;
}
mrb_value c_mul_add_add(mrb_state* mrb, mrb_value self )
{
mrb_value obj[4];
mrb_value retval;
#if 1
// Here the arguments are converted as given by format string. Therefor the mrb_value type information is not set.
auto count = mrb_get_args(mrb, "fffi", &obj[0], &obj[1], &obj[2], &obj[3] );
if(count == 4) {
#else
// The the arguments are returned as the object they are and the type information is set.
auto count = mrb_get_args(mrb, "oooo", &obj[0], &obj[1], &obj[2], &obj[3]);
if(count == 4 && mrb_float_p(obj[0]) && mrb_float_p(obj[1]) && mrb_float_p(obj[2]) && mrb_fixnum_p(obj[3])) {
#endif
double accu, num1, num2;
int i;
accu = mrb_float(obj[0]);
num1 = mrb_float(obj[1]);
num2 = mrb_float(obj[2]);
i = mrb_fixnum(obj[3]);
accu += num1 / num2 * (double)i;
retval = mrb_float_value(mrb, accu);
}
return retval;
}
mrb_value c_mul_add_add_loop(mrb_state* mrb, mrb_value self)
{
mrb_value obj[3];
mrb_value retval = mrb_float_value(mrb, 0.0);
auto count = mrb_get_args(mrb, "iff", &obj[0], &obj[1], &obj[2]);
if(count == 3) {
double accu = 0.0;
long i = mrb_fixnum(obj[0]);
double num1 = mrb_float(obj[1]);
double num2 = mrb_float(obj[2]);
for(long l = 0; l < i; ++l) {
accu += num1 / num2 * (double) l;
}
retval = mrb_float_value(mrb, accu);
}
return retval;
}
int _tmain(int argc, _TCHAR* argv[])
{
mrb_state* mrb = mrb_open();
if ( mrb == nullptr )
{
std::cout << "Embedded ruby interpretor init failed\n";
}
auto ret1 = testBinding(mrb);
eval_retcode(mrb, ret1);
// Create a function which is globally accessible
struct RClass *krn = mrb->kernel_module;
mrb_define_method(mrb, krn, "t_printstr", mrb_t_printstr, MRB_ARGS_REQ( 1 ) );
mrb_define_method(mrb, krn, "host_ver", mrb_host_ver, MRB_ARGS_REQ( 0 ) );
mrb_define_method(mrb, krn, "c_mul_add_add", c_mul_add_add, MRB_ARGS_REQ(4));
mrb_define_method(mrb, krn, "c_mul_add_add_loop", c_mul_add_add_loop, MRB_ARGS_REQ(3));
// Run the defined function in a string script.
mrb_value ret = mrb_load_string( mrb, "t_printstr 'Test coming from mruby\n'" );
eval_retcode( mrb, ret );
ret = mrb_load_string( mrb, "p 'Test pure mruby print statement'" );
eval_retcode( mrb, ret );
ret = mrb_load_string( mrb, "p host_ver" );
eval_retcode( mrb, ret );
// Read and executes the file.
FILE* f = fopen( "./test.rb", "r" );
ret = mrb_load_file( mrb, f );
fclose( f );
check_retcode( mrb );
// Call host_ver the call back to the C++ app, which resides on top level.
ret = make_call( mrb, mrb_top_self( mrb ), "host_ver" );
if( check_retcode( mrb ) ) {
// Now get the returned object and print it.
eval_retcode( mrb, ret );
}
// Call foo(), which resides on top level.
ret = make_call( mrb, mrb_top_self(mrb), "foo");
check_retcode( mrb );
// Call bar('Hallo'), which resides on top level.
ret = make_call( mrb, mrb_top_self( mrb ), "bar", "Hallo" );
check_retcode( mrb );
// Call bar(25), which resides on top level.
mrb_value intarg = mrb_fixnum_value( 25 );
ret = make_call( mrb, mrb_top_self( mrb ), "bar", 25 );
check_retcode( mrb );
// Call fun(1.25, "Hallo", 10), which resides on top level.
ret = make_call( mrb, mrb_top_self( mrb ), "fun", 1.25, "Hallo", 10 );
check_retcode( mrb );
// Call class method
// Klasse.bar
struct RClass* klasse = mrb_class_get( mrb, "Klasse" );
mrb_value kls = mrb_obj_value( klasse );
ret = make_call( mrb, kls, "bar" );
check_retcode( mrb );
/* The Following code mimics the ruby code:
* k = Klasse.new
* k.get_desc
* k.print
* k.member_var
* k.member_var = 110
* k.member_var
*/
// Instantiate class Klasse and call some of the methods.
mrb_value kls_inst = mrb_class_new_instance( mrb, 0, nullptr, klasse );
// Call method get_desc().
ret = make_call( mrb, kls_inst, "get_desc");
if( check_retcode( mrb ) ) {
// Since the get_desc returns the member @desc, show it:
eval_retcode( mrb, ret );
}
// Call method print, which calls the C++ function mrb_t_printstr
ret = make_call( mrb, kls_inst, "print");
check_retcode( mrb );
// Call default getter for the class variable member_var method.
ret = make_call( mrb, kls_inst, "member_var" );
if( check_retcode( mrb ) ) {
// Should display the member_var to be 123
eval_retcode( mrb, ret );
}
// Call default setter for the class variable member_var method.
ret = make_call( mrb, kls_inst, "member_var=", 110 );
check_retcode( mrb );
// Call default getter for the class variable member_var method. To verify the new value of 110.
ret = make_call( mrb, kls_inst, "member_var" );
if( check_retcode( mrb ) ) {
// Should display the member_var to be 110
if ( mrb_fixnum_p(ret) && mrb_fixnum(ret) == 110 ) {
// Everything as expected.
std::cout << "Got member_var = " << mrb_fixnum( ret ) << " OK!" << std::endl;
} else {
std::cout << "member_var should be 110 but is " << mrb_fixnum( ret ) << std::endl;
}
}
TimeMeasurement tmC;
auto result = c_loadcpu( 1.23, 100 );
auto res2 = result; // Do this in order not to be reordered in release mode. Otherwise the measurement is 0.
tmC.stop();
std::cout << "C result = " << result << " " << res2 << std::endl; // See above for res2
TimeMeasurement tmR;
ret = make_call(mrb, mrb_top_self(mrb), "loadcpu", 1.23, 100. );
tmR.stop();
eval_retcode(mrb, ret);
TimeMeasurement tmR2;
ret = make_call(mrb, mrb_top_self(mrb), "loadcpu2", 1.23, 100.);
tmR2.stop();
eval_retcode(mrb, ret);
TimeMeasurement tmR3;
ret = make_call(mrb, mrb_top_self(mrb), "loadcpu3", 1.23, 100.);
tmR3.stop();
eval_retcode(mrb, ret);
auto totalTimeC = tmC.diffTime<std::chrono::milliseconds>();
auto totalTimeR = tmR.diffTime<std::chrono::milliseconds>();
auto CInstr = (double) totalTimeC / 1000000.;
auto RInstr = (double) totalTimeR / 1000000.;
std::cout << "C time " << totalTimeC << " , " << CInstr << std::endl;
std::cout << "R time " << totalTimeR << " , " << RInstr << std::endl;
totalTimeR = tmR2.diffTime<std::chrono::milliseconds>();
RInstr = (double) totalTimeR / 1000000.;
std::cout << "C callback time " << totalTimeR << " , " << RInstr << std::endl;
totalTimeR = tmR3.diffTime<std::chrono::milliseconds>();
RInstr = (double) totalTimeR / 1000000.;
std::cout << "C callback2 time " << totalTimeR << " , " << RInstr << std::endl;
std::cout << "Times are in ms.\n";
mrb_close( mrb );
int i;
std::cin >> i;
return 0;
}