SWI-Prolog is well suitable for writing a wide range of complete applications without introducing other languages into the system and an even wider range when linking C/C++ coded shared objects to access external resources, in real-life Prolog is often embedded in systems written in other languages. The reasons vary. Re-use of existing code, expertise in the development team or external requirements are commonly encountered motivations.
Embedding Prolog is not a logical choice in our view. An important part of the power of Prolog can be found in its development system where retrying goals and reloading patched code on the running system speedup development considerably. In embedded system these features are often lost or severely limited due to lack of access to the interactive Prolog toplevel or inability of the rest of the application to stay synchronised with the dynamic nature of the Prolog part of the application.
If you have to embed there are several options for doing so, each with specific advantages and disadvantages.
This packages consists of the following components:
cpp_interface.pl and typedef.pl 
define directives that allow you to specify the predicates that are 
callable from C++ and their types. Only specified predicates can be 
called and only with matching types. Restricting what can be called 
greatly improves security when used in a server setting. Section 
4 describes these declarations.
cpp_codegen.pl defines the code generator. The 
code generator is used to create the C++ source for a proxy class that 
is used in the C++ client to talk to Prolog. Section 
6 describes generating the C++ proxy.
cpp_server.pl defines the 
Prolog server. See section 8 for 
details.
SWI-proxy.cpp and SWI-Proxy.h provide 
the base classes for the client proxy.
The technique used in this package are not new nor unique. Inter-language communication has been a topic in ICT for a long period, resulting in various widespread and well established solutions. The power of this solution is that it is tailured to Prolog's features such as non-deterministic predicates, lightweight, simple and fast. The weakness is of course being bound to Prolog and, currently, C++. Proxy generators can be defined for other languages, reusing most of the infrastructure except for the details of the code generation.
The design can work with other Prolog systems. The server exploits multi-threading, but with some limitations this can be changed to run in a single thread. The proxy generator is portable with some effort and it is also possible to generate the proxy with SWI-Prolog and use it with a server written in another Prolog system. The proxy itself is pure C++, knowing nothing about Prolog.
The interface definition defines the C++ callable predicates as well 
as their types and modes. The interface only deals with ground terms. 
The type language syntax is defined in the library typedef.pl 
and is based on the Mycroft/O'Keefe type language.
| (vertical bar) 
symbol.1The design allows for 
limited polymorphism, but this is not yet part of the current 
implementation. A single definition is a term whose 
arguments define the types of the arguments.
There are three primitive types: integer, float 
and atom.
Valid type declarations for our C++ interface do not use polymorphism and a fully expanded type definition consists of structures and primitive types. The argument names for compound types are derived from the type-name and usually bound to a real type using a type-alias. Here is an example:
:- type first_name = atom. :- type last_name = atom. :- type age = integer. :- type person -> person(first_name, last_name, age).
The callable predicates are specified using the library
cpp_interface.pl, which defines two directives.
void method on the C++ 
proxy class. If the predicate fails this is mapped to a C++ exception. 
This is the default.int method on the C++ proxy 
class returning FALSE if the predicate fails and TRUE 
if it succeeds.The examples below depend on the type examples above.
:- cpp_callable
        version(-atom) = [one],
        find_person_younger_than(+age, -person) = [zero_or_more].
version('0.0').
find_person_younger_than(MaxAge, person(FirstName, LastName, Age)) :-
        person(FirstName, LastName, Age),
        Age =< MaxAge.
Compound data that is to be communicated to Prolog is represented as a C++ class. This class must provide methods to fetch the components for use as a predicate input argument and with a method to create fill an instance of this class for predicate output arguments. These methods are:
long, (for Prolog integers) double (for Prolog 
floats) and the C++ std class string for atoms.
For each named field (see section 4) 
a function must be provided that extracts the field and returns the 
appropriate type. For atom typed fields the return value can be an std string 
or a plain C char*.
Below is a possible implementation for the above defined person class.
class person
{ 
public:
  char *first_name;
  char *last_name;
  int age;
  person()
  { first_name = NULL;
    last_name = NULL;
    age = -1;
  };
  ~person()
  { if ( first_name ) free(first_name);
    if ( last_name ) free(last_name);
  }
  char *get_first_name() const { return first_name; }
  char *get_last_name() const  { return last_name; }
  long  get_age() const  { return age; }
  void initialize(string fn, string ln, long years)
  { if ( first_name ) free(first_name);
    if ( last_name  ) free(last_name);
    first_name = strdup(fn.c_str());
    last_name  = strdup(ln.c_str());
    age = years;
  }
};
The C++ proxy class is automatically generated from the Prolog 
declarations using the library cpp_codegen.pl. To generate 
the code load this library in a Prolog process that has all the
cpp_callable/1 
and type declarations in place and invoke the predicate
cpp_server_code/2:
MyProxy.
Primitive data are the Prolog types integer, float and atom.
Compound data is represented as a compound term in Prolog and, unless renamed using cpp_type/2, an equally named class in C++.
The proxy for a non-deterministic predicates is a subclass of
PlQuery. The name of the class is the name of the 
predicate, unless modified using the as(Name) attribute 
with cpp_callable/1. 
A query is started by creating an instance of this class using a pointer 
to the proxy as argument. The only method defined on this class is 
::next_solution(). This method uses the same arguments as the proxy 
methods that represent deterministic queries. The following example 
fetches all functors with arity 3 defined in Prolog:
:- use_module(library(typedef)).
:- use_module(library(cpp_interface)).
:- cpp_callable
        current_functor(-atom, +integer) = [zero_or_more].
#include <iostream>
#include "myproxy.h>
int 
main(int argc, char **argv)
{ MyProxy proxy("localhost", 4224);
  try
  { between q(&proxy);
    string name;
    while ( q.next_solution(name, 3) )
    { cout << name << endl;
    }
  } catch ( PlException &ex )
  { cerr << (char *)ex;
  }
  return 0;
}
Non-deterministic queries are initiated by creating an instance of its class. The query is said to be open as long as the query object is not destroyed. New queries, both deterministic and non-deterministic can be started while another query is still open. The nested query however must be closed before more solutions can be asked from the query it is nested in.
The example below computes a table of all square roots for the numbers 1 to 100 using prolog to generate the numbers on backtracking using between/3 and access to sqrt/2. First the Prolog code, followed by the C++ code.
:- use_module(library(typedef)).
:- use_module(library(cpp_interface)).
:- cpp_callable
        between(+integer, +integer, -integer) = [zero_or_more],
        sqrt(+float, -float).
sqrt(In, Out) :- Out is sqrt(In).
#include <iostream>
#include "myproxy.h>
int 
main(int argc, char **argv)
{ SqrtProxy proxy("localhost", 4224);
  try
  { between q(&proxy);
    long l = 1;
    long h = 100;
    long i;
    while ( q.next_solution(l, h, i) )
    { double ifloat = (double)i;
      double rval;
      proxy.sqrt(ifloat, rval);
      cout << "sqrt(" << i << ") = " << rval << endl;
    }
  } catch ( PlException &ex )
  { cerr << ex;
  }
  return 0;
}
For running the server we need a Prolog process with the actual 
predicates and their declarations loaded. We load the library
cpp_server and invoke cpp_server/1:
cpp_accept that accepts new connections and, 
for each new connection, starts a new thread that handles the queries 
for the client. Options include:
The base-classes for the runtime system are installed in the 
SWI-Prolog include directory as SWI-proxy.cpp and its 
header
SWI-proxy.h. These files are not compiled into a 
library. Considering compatibility between different compilers and 
compilation models (threading, etc.) it is thought to be easier to 
include this code into the target project using the source-code.
The directory examples (installed as
.../pl/doc/packages/examples/cppproxy) contains some 
stand-alone examples as well as a README explaining how to 
compile and run the examples.
The current implementation is a demonstrator. Issues to be resolved in future versions of this package include
The system is designed to be portable using any modern C++ compiler. It has been tested on Linux using g++ 3.3.4 and MS-Windows using MSVC 6.
Installation on Unix system uses the commonly found configure,
make and make install sequence. SWI-Prolog should be 
installed before building this package. If SWI-Prolog is not installed 
as pl, the environment variable PL must be set to 
the name of the SWI-Prolog executable. Installation is now accomplished 
using:
% ./configure % make % make install
This installs the foreign library serialize in
$PLBASE/lib/$PLARCH and the Prolog library files in
$PLBASE/library and the files SWI-proxy.cpp 
and
SWI-proxy.h in $PLBASE/include, where
$PLBASE refers to the SWI-Prolog `home-directory'.
If you have successfully installed the system from source you can install this package using
% nmake /f Makefile.mak % nmake /f Makefile.mak install
If not, compile serialize.c using the command below and install the files by hand or using the makefile after setting the variable PLBASE to the base of the installed Prolog system.
% plld -o serialize serialize.c