ECLiPSe and a C or C++ main program are like threads running in a single process. Each maintains its state and methods for exchanging data and yielding control to the other thread.
The main method of sending data from C++ to ECLiPSe is by posting goals for it to solve. All posted goals are solved in conjunction with each other, and with any previously posted goals that have succeeded.
Data is passed back by binding logical variables within the goals.
Control is explicit in C++. After posting some goals, the C++ program
calls the EC_resume()
function and these goals are all
solved. A return code says whether they were successfully solved
or whether a failure occurred.
In ECLiPSe control is normally implicit. Control returns to C++ when all goals have been solved.
#include "eclipseclass.h" main() { ec_init(); /* writeln("hello world"), */ post_goal(term(EC_functor("writeln",1),"hello world")); EC_resume(); ec_cleanup(0); }
The above is an example program that posts a goal and executes it.
Using this model of communication it is possible to construct programs where execution of C++ code and search within the ECLiPSe are interleaved.
If you post a number of goals (of which some are non-deterministic) and resume the ECLiPSe execution and the goals succeed, then control returns to the C++ level. By posting a goal that fails, the ECLiPSe execution will fail back into the previous set of goals and these will succeed with a different solution.
#include "eclipseclass.h" main() { ec_init(); EC_ref Pred; post_goal(term(EC_functor("current_built_in",1),Pred)); while (EC_succeed == EC_resume()) { post_goal(term(EC_functor("writeln",1),Pred)); post_goal(EC_atom("fail")); } ec_cleanup(0); }
The above example prints all the built ins available in ECLiPSe.
When EC_resume()
returns EC_succeed
there is a solution
to a set of posted goals, and we print out the value of Pred
.
otherwise EC_resume()
returns EC_fail
to indicate
that no more solutions to any set of posted goals is available.
It is possible also to cut such search. So for example one could modify the example above to only print the 10th answer. Initially one simply fails, but at the tenth solution one cuts further choices. Then one prints the value of ’Pred’.
#include "eclipseclass.h" main() { ec_init(); EC_ref Pred,Choice; int i = 0; post_goal(term(EC_functor("current_built_in",1),Pred)); while (EC_succeed == EC_resume(Choice)) { if (i++ == 10) { Choice.cut_to(); break; } post_goal(term(EC_atom("fail"))); } post_goal(term(EC_functor("writeln",1),Pred)); EC_resume(): ec_cleanup(0); }
When EC_resume()
is called with an EC_ref
argument, this
is for data returned by the EC_resume()
If the return code is
EC_succeed
The EC_ref
is set to a choicepoint identifier
which can be used for cutting further choices at that point.
The posting of goals and building of any ECLiPSe terms in general
cannot be done asynchronously to the ECLiPSe execution. It has to
be done after the EC_resume()
function has returned.
Sometimes it may be necessary to signal some asynchronous event to ECLiPSe, for example to implement a time-out. To do this one posts a named event to ECLiPSe. At the next synchronous point within the eclipse execution, the handler for that event is invoked.
/* C++ code, possibly within a signal handler */ post_event(EC_atom("timeout")); /* ECLiPSe code */ handle_timeout(timeout) :- <appropriate action> :- set_event_handler(timeout, handle_timeout/1).
Although implicitly yielding control when a set of goals succeeds or fails is often enough, it is possible to explicitly yield control to the C++ level. This is done with the yield/2 predicate. This yields control to the calling C++ program. The arguments are used for passing data to C++ and from C++.
When yield/2 is called within ECLiPSe code, the EC_resume()
function returns the value EC_yield
so one can recognise this case.
The data passed out via the first argument of yield/2
can be accessed from C++ via the EC_ref
argument to EC_resume()
.
The data received in the second argument of yield/2 is either
the list of posted goals, or an EC_word
passed as an input
argument to EC_resume()
.
yield(out(1,2),InData),
In this example the compound term out(1,2)
is passed to C++.
If we had previously called:
EC_ref FromEclipse; result = EC_resume(FromEclipse);
then result
would be EC_yield
and FromEclipse
would
refer to out(1,2)
. If then we resumed execution with:
result = EC_resume(EC_atom("ok"),FromEclipse);
then the variable InData
on the ECLiPSe side
would be set to the atom ’ok’.
EC_resume()
can be called with two optional arguments. An
input argument that is an EC_word
and an output that is an
EC_ref
.
If the input argument is omitted, input is taken as the list of posted goals. Otherwise the input to ECLiPSe is exactly that argument.
If the output argument is present, its content depends on the value
returned by EC_resume()
. If it returns EC_succeed
it is
the choicepoint identifier. If it returns EC_yield
It is the
first argument to the yield/2 goal. If it returns EC_fail
it is not modified.