With embedded Prolog we refer to the situation where theāmain' program is not the Prolog application. Prolog is sometimes embedded in C, C++, Java or other languages to provide logic based services in a larger application. Embedding loads the Prolog engine as a library to the external language. Prolog itself only provides for embedding in the C language (compatible with C++). Embedding in Java is achieved using JPL using a C-glue between the Java and Prolog C interfaces.
The most simple embedded program is below. The interface function PL_initialise() must be called before any of the other SWI-Prolog foreign language functions described in this chapter, except for PL_initialise_hook(), PL_new_atom(), PL_new_functor() and PL_register_foreign(). PL_initialise() interprets all the command line arguments, except for the -t toplevel flag that is interpreted by PL_toplevel().
int main(int argc, char **argv) { if ( !PL_initialise(argc, argv) ) PL_halt(1); PL_halt(PL_toplevel() ? 0 : 1); }
Special consideration is required for argv[0]
. On Unix,
this argument passes the part of the command line that is used to locate
the executable. Prolog uses this to find the file holding the running
executable. The Windows version uses this to find a module
of the running executable. If the specified module cannot be found, it
tries the module libswipl.dll
, containing the Prolog
runtime kernel. In all these cases, the resulting file is used for two
purposes:
boot.prc
file from the SWI-Prolog home directory. See also qsave_program/[1,2]
and section 12.5.PL_initialise() returns 1 if all initialisation succeeded and 0 otherwise.bugVarious fatal errors may cause PL_initialise() to call PL_halt(1), preventing it from returning at all.
In most cases, argc and argv will be passed
from the main program. It is allowed to create your own argument vector,
provided
argv[0]
is constructed according to the rules above. For
example:
int main(int argc, char **argv) { char *av[10]; int ac = 0; av[ac++] = argv[0]; av[ac++] = "-x"; av[ac++] = "mystate"; av[ac] = NULL; if ( !PL_initialise(ac, av) ) PL_halt(1); ... }
Please note that the passed argument vector may be referred from Prolog at any time and should therefore be valid as long as the Prolog engine is used.
A good setup in Windows is to add SWI-Prolog's bin
directory to your PATH
and either pass a module holding a
saved state, or
"libswipl.dll"
as argv[0]
. If the Prolog state
is attached to a DLL (see the -dll option of swipl-ld),
pass the name of this DLL.
FALSE
if Prolog is not initialised and TRUE
otherwise. If the engine is initialised and argc is not NULL
,
the argument count used with PL_initialise()
is stored in argc. Same for the argument vector argv.For example, we can include the bootstrap data into an embedded executable using the steps below. The advantage of this approach is that it is fully supported by any OS and you obtain a single file executable.
% swipl -o state -c file.pl ...
% xxd -i state > state.h
#include <SWI-Prolog.h> #include "state.h" int main(int argc, char **argv) { if ( !PL_set_resource_db_mem(state, state_len) || !PL_initialise(argc, argv) ) PL_halt(1); return PL_toplevel(); }
Alternative to xxd, it is possible to use inline assembler,
e.g. the gcc incbin
instruction. Code for
gcc was provided by Roberto Bagnara on the SWI-Prolog
mailinglist. Given the state in a file state
, create the
following assembler program:
.globl _state .globl _state_end _state: .incbin "state" _state_end:
Now include this as follows:
#include <SWI-Prolog.h> #if __linux #define STATE _state #define STATE_END _state_end #else #define STATE state #define STATE_END state_end #endif extern unsigned char STATE[]; extern unsigned char STATE_END[]; int main(int argc, char **argv) { if ( !PL_set_resource_db_mem(STATE, STATE_END - STATE) || !PL_initialise(argc, argv) ) PL_halt(1); return PL_toplevel(); }
As Jose Morales pointed at https://github.com/graphitemaster/incbin, which contains a portability layer on top of the above idea.
PL_CLEANUP_NO_RECLAIM_MEMORY
PL_CLEANUP_NO_CANCEL
The return value of PL_cleanup() is one of the following:
PL_CLEANUP_CANCELED
PL_CLEANUP_SUCCESS
PL_CLEANUP_NO_RECLAIM_MEMORY
was specified this implies most of the memory was reclaimed and Prolog
may be reinitialized in the same process using PL_initialise().PL_CLEANUP_FAILED
PL_CLEANUP_RECURSIVE
PL_cleanup() allows deleting and restarting the Prolog system in the same process. In versions older than 8.5.9 this did not work. As of version 8.5.9, it works for the basic Prolog engine. Many of the plugins that contain foreign code do not implement a suitable uninstall handler and will leak memory and possibly other resources. Note that shutting Prolog down and renitializing it is slow. For almost all scenarios there are faster alternatives such as reloading modified code, using temporary modules, using threads, etc.
if ( (pid=fork()) == 0 ) { PL_cleanup_fork(); <some exec variation> }
The call behaves the same on Windows, though there is probably no meaningful application.
FALSE
if exit was cancelled by PL_cleanup().
This section applies to Unix-based environments that have signals or multithreading. The Windows version is compiled for multithreading, and Windows lacks proper signals.
We can distinguish two classes of embedded executables. There are small C/C++ programs that act as an interfacing layer around Prolog. Most of these programs can be replaced using the normal Prolog executable extended with a dynamically loaded foreign extension and in most cases this is the preferred route. In other cases, Prolog is embedded in a complex application that---like Prolog---wants to control the process environment. A good example is Java. Embedding Prolog is generally the only way to get these environments together in one process image. Java VMs, however, are by nature multithreaded and appear to do signal handling (software interrupts).
On Unix systems, SWI-Prolog installs handlers for the following signals:
EINTR
, which
gives them the opportunity to react to thread-signals.
In some cases the embedded system and SWI-Prolog may both use
SIGUSR2
without conflict. If the embedded system redefines
SIGUSR2
with a handler that runs quickly and no harm is
done in the embedded system due to spurious wakeup when initiated from
Prolog, there is no problem. If SWI-Prolog is initialised
after the embedded system it will call the handler set by the
embedded system and the same conditions as above apply. SWI-Prolog's
handler is a simple function only chaining a possibly previously
registered handler. SWI-Prolog can handle spurious
SIGUSR2
signals.
The --no-signals option can be used to inhibit all
signal processing except for SIGUSR2
. The handling of SIGUSR2
is vital for dealing with blocking system call in threads. The used
signal may be changed using the --sigalert=NUM option
or disabled using --sigalert=0
.