From: Timothy Byrd Date: 2006-08-03T09:35:17+09:00 Subject: Re: Embedded Ruby Question 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 #include "stdio.h" #include #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; }