On 28 November 2010 05:38, Neil Jerram <n...@ossau.uklinux.net> wrote:
> Peter Brett <pe...@peter-b.co.uk> writes: > >> Sure. libgeda uses direct management of memory, and the structures used >> in its document object model need to be explicitly deleted when finished >> with. I decided to use a Guile smob to represent these structures for >> access from Scheme code, with the pointer to the actual structure in >> SCM_SMOB_DATA and with the low nibble of SCM_SMOB_FLAGS indicating which >> type of DOM structure the smob references. >> >> This would have been sufficient if Scheme code had only been working >> with libgeda DOMs created and managed entirely via Scheme code. [...] > > I think your design is similar to what is outlined in the `Extending > Dia' node of the Guile manual. Were you aware of that doc before > working out your design? If not, I guess we need to make it more > prominent. If yes, I'd appreciate any suggestions you have for how it > may be improved. Yes, I code almost entirely 'by example', so having a good cookbook is critical for me. I haven't read 'Extending Dia' before, its probably more recent than the last time I set up guile bindings, some 2-3 years ago; I skimmed it briefly just now. Several comments on example code: 1) its typically not possible to wrap the C main(), so having a well-defined init() that happens some time later would be best. 2) http://www.gnu.org/software/guile/manual/html_node/Dia-Hook.html is lame. What I have to do is this: SCM rc = scm_c_catch (SCM_BOOL_T, (scm_t_catch_body) scm_c_eval_string, (void *) expr_str, SchemeEval::catch_handler_wrapper, this, SchemeEval::preunwind_handler_wrapper, this); and my catch_handler and preunwind_handler are fairly involved. Basically, if you are going to let users enter arbitrary scheme into your app, they *will* enter malformed, broken expressions, and you have to deal with these. Among other things, you have to give them a clue as to what the error was -- some sort of trace, error reporting. For me, this was implementing a REPL loop look-alike in my app. I can't say "work-alike", I think mine *almost* works-alike, but not sure. It took me a *long* time to figure out I needed the pre-unwind version of things, and even then, it took me a fair amount of effort to figure out what to put in there. Having a section showing how to implement a repl-work-alike loop in one's app, with reasonable debugging/stack-printing output, would be nice to have. Figuring out how to do this one one's own requires a lot of tenacity. For the record, I've attched the code I wrote to do the above (and to multi-thread, which someone later on disabled :-( Its in C++, sorry about that, don't blame me.) --linas
/* * SchemeEval.h * * Simple scheme expression evaluator * Copyright (c) 2008 Linas Vepstas <li...@linas.org> */ #ifndef OPENCOG_SCHEME_EVAL_H #define OPENCOG_SCHEME_EVAL_H #ifdef HAVE_GUILE #include <string> #include <pthread.h> #include <libguile.h> #include <opencog/atomspace/Handle.h> namespace opencog { class SchemeEval { private: // Initialization stuff void init(void); static void * c_wrap_init(void *); void per_thread_init(void); void thread_lock(void); void thread_unlock(void); // destructor stuff void finish(void); static void * c_wrap_finish(void *); // Things related to evaluation std::string do_eval(const std::string &); static void * c_wrap_eval(void *); static void * c_wrap_eval_h(void *); const std::string *pexpr; std::string answer; std::string input_line; bool pending_input; // straight-up evaluation static SCM wrap_scm_eval(void *); SCM do_scm_eval(SCM); SCM do_scm_eval_str(const std::string &); // Handle apply Handle do_apply(const std::string& func, Handle varargs); SCM do_apply_scm(const std::string& func, Handle varargs); Handle hargs; static void * c_wrap_apply(void *); static void * c_wrap_apply_scm(void *); // Error handling stuff SCM error_string_port; SCM captured_stack; static SCM preunwind_handler_wrapper(void *, SCM, SCM); static SCM catch_handler_wrapper(void *, SCM, SCM); SCM preunwind_handler(SCM, SCM); SCM catch_handler(SCM, SCM); bool caught_error; // printing of basic types static std::string prt(SCM); // output port SCM outport; SCM saved_outport; // Make constructor, destructor private; force // everyone to use the singleton instance, for now. SchemeEval(void); ~SchemeEval(); static SchemeEval* singletonInstance; public: std::string eval(const std::string &); Handle eval_h(const std::string &); Handle apply(const std::string& func, Handle varargs); std::string apply_generic(const std::string& func, Handle varargs); bool input_pending(void); void clear_pending(void); bool eval_error(void); // Someone thinks that there some scheme threading bug somewhere, // and the current hack around this is to use a singleton instance. static SchemeEval& instance(void) { if (!singletonInstance) singletonInstance = new SchemeEval(); return *singletonInstance; } }; } #else /* HAVE_GUILE */ #include <opencog/atomspace/Handle.h> namespace opencog { class SchemeEval { private: static SchemeEval* singletonInstance; public: std::string eval(const std::string &s) { return ""; } Handle eval_h(const std::string &s) { return Handle::UNDEFINED; } Handle apply(const std::string &s, Handle args) { return Handle::UNDEFINED; } std::string apply_generic(const std::string& f, Handle args) { return ""; } bool input_pending(void) { return false; } void clear_pending(void) {} // If guile is not installed, then *every* call to eval_error() // must report that an error occurred! bool eval_error(void) { return true; } static SchemeEval& instance(void) { if (!singletonInstance) singletonInstance = new SchemeEval(); return *singletonInstance; } }; } #endif/* HAVE_GUILE */ #endif /* OPENCOG_SCHEME_EVAL_H */
/* * SchemeExec.cc * * Execute ExecutionLink's * Copyright (c) 2009 Linas Vepstas <linasveps...@gmail.com> */ #ifdef HAVE_GUILE #include <libguile.h> #include <opencog/atomspace/Link.h> #include <opencog/server/CogServer.h> #include <boost/shared_ptr.hpp> #include "SchemeEval.h" #include "SchemeSmob.h" using namespace opencog; /** * do_apply -- apply named function func to arguments in ListLink * It is assumed that varargs is a ListLink, containing a list of * atom handles. This list is unpacked, and then the fuction func * is applied to them. If the function returns an atom handle, then * this is returned. */ Handle SchemeEval::do_apply(const std::string &func, Handle varargs) { // Apply the function to the args SCM sresult = do_apply_scm (func, varargs); // If the result is a handle, return the handle. if (!SCM_SMOB_PREDICATE(SchemeSmob::cog_handle_tag, sresult)) { return Handle::UNDEFINED; } return SchemeSmob::scm_to_handle(sresult); } /** * do_apply_scm -- apply named function func to arguments in ListLink * It is assumed that varargs is a ListLink, containing a list of * atom handles. This list is unpacked, and then the fuction func * is applied to them. The SCM value returned by the function is returned. */ SCM SchemeEval::do_apply_scm( const std::string& func, Handle varargs ) { SCM sfunc = scm_from_locale_symbol(func.c_str()); SCM expr = SCM_EOL; // If there were args, pass the args to the function. boost::shared_ptr<Link> largs = cogserver().getAtomSpace()->cloneLink(varargs); if (largs) { const std::vector<Handle> &oset = largs->getOutgoingSet(); size_t sz = oset.size(); for (int i=sz-1; i>=0; i--) { Handle h = oset[i]; SCM sh = SchemeSmob::handle_to_scm(h); expr = scm_cons(sh, expr); } } expr = scm_cons(sfunc, expr); return do_scm_eval(expr); } #endif /* ===================== END OF FILE ============================ */