Previous Up

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.


Previous Up