guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Need help embedding Guile


From: Dimitris Papavasiliou
Subject: Need help embedding Guile
Date: Tue, 21 Dec 2021 11:12:54 +0000

Hi all,

I'm in the process of embedding Guile in an application and although I seem to
have the essentials working, I'd appreciate some confirmation of the validity of
my approach and also some tips on a couple of loose ends.

I won't bore you with the specifics of my application; for the purposes of the
discussion, its most important characteristic, is that it uses Guile as a
frontend of sorts.  By this, I mean that a Scheme program is executed, which
creates objects in the C (well actually, and out of necessity, C++) domain.
These objects represent geometric operations, in the form of a graph and are
evaluated once the Scheme program has terminated.  Since the evaluation can take
a long time and the Scheme code itself, in simply creating nodes in the graph,
is expected to run to completion quite quickly, even though it can be
conceptually complex, the emphasis on the Scheme side is on debugability instead
of efficiency.

My aim, is to be able to load a Scheme program from a file, run it to have the
graph created and then clean up.  On error, I'd like to print out diagnostic
information in the form of an error message with as accurate as possible source
location and a stack trace.  (I'd also like to print the latter with my own
formatting to match rest of the output of the application.)

Although perhaps other approaches are possible, I have, for now, chosen to leave
memory management to the C++ side, so that my foreign objects need custom
finalization.  The basic layout of my current implementation, with uninteresting
portions left out, is the following (where `run_scheme' is called by the main
program to run a Scheme script):

struct context {
    char *input, **first, **last;
};

int run_scheme(const char *input, char **first, char **last)
{
    struct context context = {const_cast<char *>(input), first, last};

    scm_with_guile(&run_scheme_from_guile, &context);

    return 0;
}

static void *run_scheme_from_guile(void *data)
{
    struct context *context = static_cast<struct context *>(data);

    scm_set_automatic_finalization_enabled(0);

    // Define some foreign objects types and subroutines.
    // [...]

    scm_set_program_arguments(
        context->last - context->first, context->first, context->input);

    scm_c_catch(SCM_BOOL_T,
                run_body, reinterpret_cast<void *>(context),
                post_handler, nullptr,
                pre_handler, nullptr);

    scm_gc();
    scm_run_finalizers();

    return nullptr;
}

static SCM run_body(void *data)
{
    struct context *context = static_cast<struct context *>(data);

    scm_primitive_eval(
        scm_list_2(
            scm_from_latin1_symbol("load"),
            scm_from_latin1_string(context->input)));

    return SCM_UNSPECIFIED;
}

static SCM pre_handler(void *data, SCM key, SCM args)
{
    SCM s = scm_make_stack(SCM_BOOL_T, SCM_EOL);
    SCM p = scm_current_error_port();

    scm_print_exception(p, SCM_BOOL_F, key, args);
    scm_display_backtrace(s, p, SCM_BOOL_F, SCM_BOOL_F);

    return SCM_BOOL_T;
}

static SCM post_handler(void *data, SCM key, SCM args)
{
    return SCM_BOOL_T;
}

Actually, my code in `pre_handler' is not quite what is shown above, as I print
the stack with my own formatting, but let's leave that for later.  As I said,
this seems to be working, but certain points are unclear to me after reading all
the documentation I could find and snooping around in Guile's source code:

1. The manual is not very specific about how and when finalizers are run.  The
   approach above seems to correctly finalize all objects created as the Scheme
   code executes, but if references are kept, say via (define), they are not
   finalized and I get memory leaks.  Is there some way to arrange for the
   complete deinitialization of Guile after I've finished evaluating Scheme code
   and making sure that all finalizers are run?

2. If, in `run_body', I simply do

       scm_c_primitive_load(context->input);

   then the code is evaluated, but on error I get no locations in the stack
   trace.  The error is said to have occurred "in an unknown file" with no line
   numbers.  Evaluating `load' as shown above, seems to produce proper source
   locations in the stack trace.  Is there something else I should be preferably
   doing?

3. More generally, is there a preferable way to go about embedding Guile for my
   use case?

Thanks in advance for any pointers,
Dimitris




reply via email to

[Prev in Thread] Current Thread [Next in Thread]