redirect stdout for embedded Ruby

P

Phlip

Rubinistas:

#include "C:\ruby\lib\ruby\1.8\i386-mswin32\ruby.h"
#pragma comment(lib, "C:/ruby/lib/msvcrt-ruby18.lib")

int main(int argc, char* argv[])
{
ruby_init();
ruby_script("embedded");

FILE * out = freopen("c:/temp/redirect.txt", "w", stdout);
FILE * err = freopen("c:/temp/redirect.txt", "w", stderr);

rb_eval_string("puts 'hello world'\n");
return 0;
}

That code expects to evaluate a Ruby script, inside C/C++, and capture its
output into c:/temp/redirect.txt.

Ideally, I'd like to get the results into a stream, so I can pull it in
realtime as Ruby pushes it. But reading that temp file is an acceptable
compromise.

However, that code simply crashes, deep inside malloc.c inside Ruby. Anyone
know why, or if there's a better way?
 
J

Joel VanderWerf

Phlip said:
Rubinistas:

#include "C:\ruby\lib\ruby\1.8\i386-mswin32\ruby.h"
#pragma comment(lib, "C:/ruby/lib/msvcrt-ruby18.lib")

int main(int argc, char* argv[])
{
ruby_init();
ruby_script("embedded");

FILE * out = freopen("c:/temp/redirect.txt", "w", stdout);
FILE * err = freopen("c:/temp/redirect.txt", "w", stderr);

rb_eval_string("puts 'hello world'\n");
return 0;
}

That code expects to evaluate a Ruby script, inside C/C++, and capture its
output into c:/temp/redirect.txt.

Ideally, I'd like to get the results into a stream, so I can pull it in
realtime as Ruby pushes it. But reading that temp file is an acceptable
compromise.

However, that code simply crashes, deep inside malloc.c inside Ruby. Anyone
know why, or if there's a better way?

What about something based on

rb_eval_string("$stdout.reopen ...");

or

rb_eval_string("$stdout = File.open(...)");
 
P

Phlip

What about something based on

rb_eval_string("$stdout.reopen ...");

or

rb_eval_string("$stdout = File.open(...)");

Thanks! Leave it to Ruby to rescue us from weird situations, huh?

While I type that in, can we think of a way to, for example, publish a
function from C to Ruby (I know how to do that part) which absorbs
everything 'write()' sends to the file handle? That would provide the async
read situation...
 
P

Phlip

Joel said:
rb_eval_string("$stdout.reopen ...");

Here ya go, homeboy:

#include <string>
#include <iostream>
#include <fstream>
#include <assert.h>
#include "C:\ruby\lib\ruby\1.8\i386-mswin32\ruby.h"
#pragma comment(lib, "C:/ruby/lib/msvcrt-ruby18.lib")

using std::string;
using std::cout;
using std::endl;
using std::ifstream;
using std::getline;

static string error_print(int state)
{
VALUE eclass;
VALUE einfo;
char buff[BUFSIZ];

#define TAG_RETURN 0x1
#define TAG_BREAK 0x2
#define TAG_NEXT 0x3
#define TAG_RETRY 0x4
#define TAG_REDO 0x5
#define TAG_RAISE 0x6
#define TAG_THROW 0x7
#define TAG_FATAL 0x8
#define TAG_MASK 0xf

switch (state) {
case TAG_RETURN: return ("E267: unexpected return");
case TAG_NEXT: return ("E268: unexpected next");
case TAG_BREAK: return ("E269: unexpected break");
case TAG_REDO: return ("E270: unexpected redo");
case TAG_RETRY: return ("E271: retry outside of rescue clause");
case TAG_RAISE:
case TAG_FATAL:
eclass = CLASS_OF(ruby_errinfo);
einfo = rb_obj_as_string(ruby_errinfo);
if (eclass == rb_eRuntimeError && RSTRING(einfo)->len == 0) {
return ("E272: unhandled exception");
}
else {
VALUE epath;
char *p;

epath = rb_class_path(eclass);
snprintf(buff, BUFSIZ, "%s: %s",
RSTRING(epath)->ptr, RSTRING(einfo)->ptr);
p = strchr(buff, '\n');
if (p) *p = '\0';
return (buff);
}

default:
snprintf(buff, BUFSIZ, _("E273: unknown longjmp status %d"), state);
return (buff);

}
}


int main(int argc, char* argv[])
{

#if defined(NT)
NtInitialize(&argc, &argv);
#endif

ruby_init();
ruby_script("embedded");

int state(0);
rb_eval_string_protect("$stdout.reopen(File.open('c:/temp/redirect.txt',
'w'))\n", &state);
if (state) { cout << error_print(state) << endl; return 1; }

rb_eval_string_protect("puts 'hello world'\n", &state);
if (state) { cout << error_print(state) << endl; return 1; }

rb_eval_string_protect("$stdout.flush()\n", &state);
if (state) { cout << error_print(state) << endl; return 1; }

ifstream file ("c:/temp/redirect.txt");
string line;
getline(file, line);
assert ("hello world" == line);

return 0;
}

The two clues were - protect the evaluation, so when it dies on "file not
open" we get a clean error message, and redirect inside Ruby, not outside
where it's hard.

Now I work on replacing the temporary file with a data sink back into my C++
source.
 
P

Phlip

Phlip said:
Now I work on replacing the temporary file with a data sink back into my C++
source.

That was so simple, I have probably screwed it up!

static VALUE sink(VALUE self, VALUE input)
{
char * got = StringValueCStr(input);
cout << got << endl; // todo - replace this with something that
routes the data where I need it
return Qnil;
}
....
ruby_init();
ruby_script("embedded");

rb_define_global_function("sink", RUBY_METHOD_FUNC(sink), 1);

int state(0);
rb_eval_string_protect(
"class MockStream \n"
" def write(x) \n"
" sink(x) \n"
" end \n"
"end \n"
"$stdout = MockStream.new()\n"
// "$stdout.reopen(File.open('c:/temp/redirect.txt', 'w'))\n"
, &state);
if (state) { cout << error_print(state) << endl; return 1; }

rb_eval_string_protect("puts 'hello world'\n", &state);
if (state) { cout << error_print(state) << endl; return 1; }

It works for 'puts', but I don't know

I will proceed with it until something croaks. The _protect makes this
strategy safe!
 
N

nobu.nokada

Hi,

At Wed, 25 Aug 2004 15:50:45 +0900,
Phlip wrote in [ruby-talk:110422]:
That was so simple, I have probably screwed it up!

static VALUE sink(VALUE self, VALUE input)
{
char * got = StringValueCStr(input);
cout << got << endl; // todo - replace this with something that
routes the data where I need it
return Qnil;
}
....
ruby_init();
ruby_script("embedded");

rb_define_global_function("sink", RUBY_METHOD_FUNC(sink), 1);

You don't need a global function.

VALUE out = rb_class_new_instance(0, 0, rb_cObject);
rb_define_singleton_method(out, "write", RUBY_METHOD_FUNC(sink), 1);
rb_stdout = out;
rb_stderr = out;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top