2.3 Control flow
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.
2.3.1 Control flow and search
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.
2.3.2 Asynchronous events
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).
2.3.3 The yield-resume model
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'.
2.3.4 Summary of EC_resume() arguments
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.