Redirect stdout for embedded Ruby

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?

···

--
  Phlip
  http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces

Phlip wrote:

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(...)");

Joel VanderWerf wrote:

> 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.

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...

···

--
  Phlip
  http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces

Joel VanderWerf wrote:

   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.

···

--
  Phlip
  http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces

Phlip wrote:

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!

···

--
  Phlip
  http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces

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;

···

--
Nobu Nakada