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