Brian Campbell wrote:
I want to embed Ruby in my C++ application.
Here is some sample code I'd come up with (for 1.8.2, I think). You
may need to tweak it a bit.
-- Timothy
// r_in_c.cpp : Sample program for embedding Ruby in C.
//
//#include "stdafx.h"
#include <tchar.h>
#include "stdio.h"
#include <string>
#pragma warning(disable: 4312)
#include "ruby.h"
//#include "st.h"
void print_ruby_value(int v, int indent, const char* str)
{
VALUE val = (VALUE)(v);
int type = TYPE(v);
#if 1
switch (type) {
case T_FIXNUM:
printf("%*s`%s` ==> fixnum, value = %d\n", indent, "", str,
FIX2INT(v));
break;
case T_NIL:
printf("%*s`%s` ==> nil\n", indent, "", str);
break;
case T_FALSE:
printf("%*s`%s` ==> false\n", indent, "", str);
break;
case T_TRUE:
printf("%*s`%s` ==> true\n", indent, "", str);
break;
case T_UNDEF:
printf("%*s`%s` ==> undef\n", indent, "", str);
break;
case T_SYMBOL:
printf("%*s`%s` ==> symbol (%d)\n", indent, "", str,
SYM2ID(v));
break;
case T_STRING:
printf("%*s`%s` ==> string `%*s`\n", indent, "", str,
RSTRING(v)->len, RSTRING(v)->ptr);
break;
case T_FLOAT:
printf("%*s`%s` ==> float `%f`\n", indent, "", str,
RFLOAT(v)->value);
break;
case T_BIGNUM:
{
VALUE s = rb_String(val);
printf("%*s`%s` ==> bignum `%*s`\n", indent, "", str,
RSTRING(s)->len, RSTRING(s)->ptr);
}
break;
case T_REGEXP:
printf("%*s`%s` ==> regexp `%*s`\n", indent, "", str,
RREGEXP(v)->len, RREGEXP(v)->str);
break;
case T_ARRAY:
{
char buffer[20];
long len = RARRAY(v)->len;
VALUE* ptr = RARRAY(v)->ptr;
printf("%*s`%s` ==> array (%d):\n", indent, "", str, len);
for (long i = 0; i < RARRAY(v)->len; ++i)
{
sprintf(buffer, "a[%d]", i);
print_ruby_value(ptr[i], indent+4, "default value");
}
}
break;
case T_HASH:
{
RHash* h = RHASH(v);
printf("%*s`%s` ==> hash (iter_lev %d):\n", indent, "", str,
h->iter_lev);
print_ruby_value(h->ifnone, indent+4, "not found value");
struct st_table *tbl = h->tbl;;
printf("%*sbins = %d, entries = %d\n", indent+4, "",
tbl->num_bins, tbl->num_entries);
}
break;
default:
printf("%*s`%s` ==> class %d\n", indent, "", str, type);
break;
}
#else
switch (type) {
case T_NIL:
printf("%*s`%s` ==> nil\n", indent, "", str);
break;
case T_ARRAY:
val = rb_ary_join(val, rb_str_new2(", "));
printf("%*s`%s` ==> type(%d) `[%*s]`\n", indent, "", str, type,
RSTRING(val)->len, RSTRING(val)->ptr);
break;
case T_REGEXP:
val = rb_String(val);
printf("%*s`%s` ==> type(%d) `/%*s/`\n", indent, "", str, type,
RSTRING(val)->len, RSTRING(val)->ptr);
break;
default:
val = rb_String(val);
printf("%*s`%s` ==> type(%d) `%*s`\n", indent, "", str, type,
RSTRING(val)->len, RSTRING(val)->ptr);
}
#endif
if (indent == 0) {
printf("\n");
}
}
// Returns output of command as a string
//
// see rb_f_backquote() in io.c
//
// needs to set exit code of command into rb_last_status
//
static VALUE m_backquote(VALUE self, VALUE commandVal)
{
VALUE arr = Qnil;
char* commandStr = StringValueCStr(commandVal);
if (commandStr) {
printf("Executing backquote command on `%s`\n", commandStr);
// This should be the captured output of the command.
// I'll just reverse the string as the command value, here.
//
size_t len = strlen(commandStr);
std::string ss(len, ' ');
for (size_t i = 0, j = len - 1; i < len; ++i, --j) {
ss[i] = commandStr[j];
}
arr = rb_str_new2(ss.c_str());
}
else {
printf("Error: cannot execute object\n");
}
return arr;
}
// Executes _cmd_ in a subshell, returning true (Qtrue) if the command
// was found and ran successfully, false (Qfalse) otherwise.
//
// passed in elements may be arrays - may want to flatten and then
// join with " "s between.
//
// See rb_f_system() in process.c
//
// needs to set exit code of command into rb_last_status
//
static VALUE m_system(int argc, VALUE *argv)
{
printf("Called Kernel#system on:\n");
for (int i = 0; i < argc; ++i) {
VALUE s = rb_String(argv[i]);
printf(" %2d. `%*s`\n", i, RSTRING(s)->len, RSTRING(s)->ptr);
}
return Qtrue;
}
// Might want to have separate functions for stdout and stderr, so they
// can be separated...
//
static VALUE m_write_stdout(VALUE self, VALUE input)
{
if (TYPE(input) == T_STRING) {
printf("We have captured (stdout): \"%*s\"\n",
RSTRING(input)->len, RSTRING(input)->ptr);
// Return the number of bytes written
return
rb_fix_new(RSTRING(input)->len*sizeof(RSTRING(input)->ptr[0]));
}
else {
printf("Error: cannot write object to stdout\n");
return Qnil;
}
}
static VALUE m_write_stderr(VALUE self, VALUE input)
{
// Need to use rb_string_value if it can contain embedded nuls.
char * got = StringValueCStr(input);
printf("We have captured (stderr): \"%s\"\n", got);
// Return the number of bytes written
return rb_fix_new(strlen(got)*sizeof(got[0]));
}
int _tmain(int argc, _TCHAR* argv)
{
NtInitialize(&argc, &argv);
ruby_init();
ruby_script("embedded");
rb_define_global_function("`", RUBY_METHOD_FUNC(m_backquote), 1);
rb_define_global_function("system", RUBY_METHOD_FUNC(m_system),
-1);
rb_define_singleton_method(rb_stdout, "write",
RUBY_METHOD_FUNC(m_write_stdout), 1);
rb_define_singleton_method(rb_stderr, "write",
RUBY_METHOD_FUNC(m_write_stderr), 1);
char* eval_strings = {
"3",
"nil",
"true",
"false",
"5+6",
"?c",
":fred",
"'hello'",
"\"hello\"",
"'hello there'",
"'123'*3",
"3.8",
"x = 123_456",
"x * x",
"\"#{x * x}\"",
"111_111_111**2",
"-111_111_111**3",
"[1, 'two', 3.0]",
"[[1,2,3],[4,5,6],[7,8,9]]",
"/r[iou]se/",
"`dir c:\\\\windows`",
"h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' =>
'asinine' } ",
"h.length",
"h['dog']",
"h['cow'] = 'bovine' ",
"h[12] = 'dodecine' ",
"h['cat'] = 99 ",
"h",
"puts 'this is a test'",
"$stderr.puts 'writing to stderr'",
"system('TITLE', '4NT/Ruby')",
"system('DELAY 3 & TITLE Changed window title from Ruby')",
"5",
};
const int eval_strings_len = sizeof(eval_strings) /
sizeof(eval_strings[0]);
int status = 0;
for (int i = 0; i < eval_strings_len; ++i) {
status = rb_eval_string(eval_strings[i]);
print_ruby_value(status, 0, eval_strings[i]);
}
ruby_cleanup(0);
return 0;
}