## File: pl2cpp.doc

package info (click to toggle)
swi-prolog 8.0.2+dfsg-3+deb10u1
 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211 \documentclass[11pt,a4paper]{article} \usepackage{times} \usepackage{pl} \usepackage{plpage} %\usepackage{xpce} \usepackage{html} \sloppy \onefile \htmloutput{.} % Output directory \htmlmainfile{pl2cpp} % Main document file \bodycolor{white} % Page colour \renewcommand{\runningtitle}{A C++ interface to SWI-Prolog} \makeindex \begin{document} \title{A C++ interface to SWI-Prolog} \author{Jan Wielemaker \\ VU University of Amsterdam \\ The Netherlands \\ E-mail: \email{jan@swi-prolog.org}} \maketitle \begin{abstract} This document describes a C++ interface to SWI-Prolog. SWI-Prolog could be used with C++ for a very long time, but only by calling the extern "C" functions of the C-interface. The interface described herein provides a true C++ layer around the C-interface for much more concise and natural programming from C++. The interface deals with automatic type-conversion to and from native C data-types, transparent mapping of exceptions, making queries to Prolog and registering foreign predicates. \end{abstract} \vfill \vfill \vfill \newpage \tableofcontents \newpage \section{Introduction} \label{sec:cpp-intro} C++ provides a number of features that make it possible to define a much more natural and concise interface to dynamically typed languages than plain C does. Using programmable type-conversion (\jargon{casting}), native data-types can be translated automatically into appropriate Prolog types, automatic destructors can be used to deal with most of the cleanup required and C++ exception handling can be used to map Prolog exceptions and interface conversion errors to C++ exceptions, which are automatically mapped to Prolog exceptions as control is turned back to Prolog. \subsection*{Competing interfaces} Volker Wysk has defined an alternative C++ mapping based on templates and compatible to the STL framework. See \url{http://www.volker-wysk.de/swiprolog-c++/index.html}. \subsection*{Acknowledgements} I would like to thank Anjo Anjewierden for comments on the definition, implementation and documentation of this package. \section{Overview} \label{sec:cpp-overview} The most useful area for exploiting C++ features is type-conversion. Prolog variables are dynamically typed and all information is passed around using the C-interface type \type{term_t}. In C++, \type{term_t} is embedded in the \jargon{lightweight} class \class{PlTerm}. Constructors and operator definitions provide flexible operations and integration with important C-types (\type{char *}, \type{wchar_t*}, \type{long} and \type{double}). The list below summarises the classes defined in the C++ interface. \begin{description} \classitem{PlTerm} Generic Prolog term. Provides constructors and operators for conversion to native C-data and type-checking. \classitem{PlString} Subclass of \class{PlTerm} with constructors for building Prolog string objects. \classitem{PlCodeList} Subclass of \class{PlTerm} with constructors for building Prolog lists of ASCII values. \classitem{PlCharList} Subclass of \class{PlTerm} with constructors for building Prolog lists of one-character atoms (as atom_chars/2). \classitem{PlCompound} Subclass of \class{PlTerm} with constructors for building compound terms. \classitem{PlTail} SubClass of \class{PlTerm} for building and analysing Prolog lists. \classitem{PlTermv} Vector of Prolog terms. See PL_new_term_refs(). the \const{[]} operator is overloaded to access elements in this vector. \class{PlTermv} is used to build complex terms and provide argument-lists to Prolog goals. \classitem{PlException} Subclass of \class{PlTerm} representing a Prolog exception. Provides methods for the Prolog communication and mapping to human-readable text representation. \classitem{PlTypeError} Subclass of \class{PlException} for representing a Prolog \except{type_error} exception. \classitem{PlDomainError} Subclass of \class{PlException} for representing a Prolog \except{domain_error} exception. \classitem{PlExistenceError} Subclass of \class{PlException} for representing a Prolog \except{existence_error} exception. \classitem{PlPermissionError} Subclass of \class{PlException} for representing a Prolog \except{permission_error} exception. \classitem{PlAtom} Allow for manipulating atoms in their internal Prolog representation for fast comparison. \classitem{PlQuery} Represents opening and enumerating the solutions to a Prolog query. \classitem{PlFrame} This utility-class can be used to discard unused term-references as well as to do \jargon{data-backtracking}'. \classitem{PlEngine} This class is used in \jargon{embedded} applications (applications where the main control is held in C++). It provides creation and destruction of the Prolog environment. \classitem{PlRegister} The encapsulation of PL_register_foreign() is defined to be able to use C++ global constructors for registering foreign predicates. \end{description} The required C(++) function header and registration of a predicate is arranged through a macro called \cfuncref{PREDICATE}{}. \section{Examples} \label{sec:cpp-examples} Before going into a detailed description of the C++ classes we present a few examples illustrating the feel' of the interface. \subsection{Hello(World)} \label{sec:cpp-hello-world} This simple example shows the basic definition of the predicate hello/1 and how a Prolog argument is converted to C-data: \begin{code} PREDICATE(hello, 1) { cout << "Hello " << (char *)A1 << endl; return TRUE; } \end{code} The arguments to PREDICATE() are the name and arity of the predicate. The macros A provide access to the predicate arguments by position and are of the type \class{PlTerm}. Casting a \class{PlTerm} to a \type{char *} or \type{wchar_t *} provides the natural type-conversion for most Prolog data-types, using the output of write/1 otherwise: \begin{code} ?- hello(world). Hello world Yes ?- hello(X) Hello _G170 X = _G170 \end{code} \subsection{Adding numbers} \label{sec:cpp-ex-adding-numbers} This example shows arithmetic using the C++ interface, including unification, type-checking and conversion. The predicate add/3 adds the two first arguments and unifies the last with the result. \begin{code} PREDICATE(add, 3) { return A3 = (long)A1 + (long)A2; } \end{code} Casting a \class{PlTerm} to a \type{long} performs a PL_get_long() and throws a C++ exception if the Prolog argument is not a Prolog integer or float that can be converted without loss to a \type{long}. The \const{=} operator of \class{PlTerm} is defined to perform unification and returns \const{TRUE} or \const{FALSE} depending on the result. \begin{code} ?- add(1, 2, X). X = 3. ?- add(a, 2, X). [ERROR: Type error: integer' expected, found a'] Exception: ( 7) add(a, 2, _G197) ? \end{code} \subsection{Average of solutions} \label{sec:cpp-ex-average} This example is a bit harder. The predicate average/3 is defined to take the template \mbox{average(+Var, :Goal, -Average)}, where \arg{Goal} binds \arg{Var} and will unify \arg{Average} with average of the (integer) results. \class{PlQuery} takes the name of a predicate and the goal-argument vector as arguments. From this information it deduces the arity and locates the predicate. the member-function next_solution() yields \const{TRUE} if there was a solution and \const{FALSE} otherwise. If the goal yielded a Prolog exception it is mapped into a C++ exception. \begin{code} PREDICATE(average, 3) { long sum = 0; long n = 0; PlQuery q("call", PlTermv(A2)); while( q.next_solution() ) { sum += (long)A1; n++; } return A3 = (double)sum/(double)n; } \end{code} \section{The class PlTerm} \label{sec:cpp-plterm} As we have seen from the examples, the \class{PlTerm} class plays a central role in conversion and operating on Prolog data. This section provides complete documentation of this class. \subsection{Constructors} \label{sec:cpp-plterm-constructurs} \begin{description} \constructor{PlTerm}{} Creates a new initialised term (holding a Prolog variable). \constructor{PlTerm}{term_t t} Converts between the C-interface and the C++ interface by turning the term-reference into an instance of \class{PlTerm}. Note that, being a lightweight class, this is a no-op at the machine-level! \constructor{PlTerm}{const char *text} Creates a term-references holding a Prolog atom representing \arg{text}. \constructor{PlTerm}{const wchar_t *text} Creates a term-references holding a Prolog atom representing \arg{text}. \constructor{PlTerm}{const PlAtom \&atom} Creates a term-references holding a Prolog atom from an atom-handle. \constructor{PlTerm}{long n} Creates a term-references holding a Prolog integer representing \arg{n}. \constructor{PlTerm}{double f} Creates a term-references holding a Prolog float representing \arg{f}. \constructor{PlTerm}{void *ptr} Creates a term-references holding a Prolog pointer. A pointer is represented in Prolog as a mangled integer. The mangling is designed to make most pointers fit into a \jargon{tagged-integer}. Any valid pointer can be represented. This mechanism can be used to represent pointers to C++ objects in Prolog. Please note that myclass' should define conversion to and from \type{void *}. \begin{code} PREDICATE(make_my_object, 1) { myclass *myobj = new myclass(); return A1 = (void *)myobj; } PREDICATE(free_my_object, 1) { myclass *myobj = (void *)A1; delete(myobj); return TRUE; } \end{code} \end{description} \subsection{Casting PlTerm to native C-types} \label{sec:cpp-plterm-casting} \class{PlTerm} can be casted to the following types: \begin{description} \cppcast{PlTerm}{term_t} This cast is used for integration with the C-interface primitives. \cppcast{PlTerm}{long} Yields a \type{long} if the \class{PlTerm} is a Prolog integer or float that can be converted without loss to a long. throws a \except{type_error} exception otherwise. \cppcast{PlTerm}{int} Same as for \type{long}, but might represent fewer bits. \cppcast{PlTerm}{double} Yields the value as a C double if \class{PlTerm} represents a Prolog integer or float. \cppcast{PlTerm}{wchar_t *} \nodescription \cppcast{PlTerm}{char *} Converts the Prolog argument using PL_get_chars() using the flags \const{CVT_ALL|CVT_WRITE|BUF_RING}, which implies Prolog atoms and strings are converted to the represented text. All other data is handed to write/1. If the text is static in Prolog, a direct pointer to the string is returned. Otherwise the text is saved in a ring of 16 buffers and must be copied to avoid overwriting. \cppcast{PlTerm}{void *} Extracts pointer value from a term. The term should have been created by PlTerm::PlTerm(void*). \end{description} \subsection{Unification} \label{sec:cpp-plterm-unification} \begin{description} \cfunction{int}{PlTerm::operator =}{Type} The operator \const{=} is defined for the \arg{Types} \class{PlTerm}, \type{long}, \type{double}, \type{char *}, \type{wchar_t*} and \class{PlAtom}. It performs Prolog unification and returns \const{TRUE} if successful and \const{FALSE} otherwise. The boolean return-value leads to somewhat unconventional-looking code as normally, assignment returns the value assigned in C. Unification however is fundamentally different to assignment as it can succeed or fail. Here is a common example. \begin{code} PREDICATE(hostname, 1) { char buf[32]; if ( gethostname(buf, sizeof(buf)) == 0 ) return A1 = buf; return FALSE; } \end{code} \end{description} \subsection{Comparison} \label{sec:cpp-plterm-comparison} \begin{description} \cfunction{int}{PlTerm::operator ==}{const PlTerm \&t} \nodescription \cfunction{int}{PlTerm::operator !=}{const PlTerm \&t} \nodescription \cfunction{int}{PlTerm::operator $<$}{const PlTerm \&t} \nodescription \cfunction{int}{PlTerm::operator $>$}{const PlTerm \&t} \nodescription \cfunction{int}{PlTerm::operator $<=$}{const PlTerm \&t} \nodescription \cfunction{int}{PlTerm::operator $>=$}{const PlTerm \&t} Compare the instance with \arg{t} and return the result according to the Prolog defined \jargon{standard order of terms}. \cfunction{int}{PlTerm::operator ==}{long num} \nodescription \cfunction{int}{PlTerm::operator !=}{long num} \nodescription \cfunction{int}{PlTerm::operator $<$}{long num} \nodescription \cfunction{int}{PlTerm::operator $>$}{long num} \nodescription \cfunction{int}{PlTerm::operator $<=$}{long num} \nodescription \cfunction{int}{PlTerm::operator $>=$}{long num} Convert \class{PlTerm} to a \type{long} and perform standard C-comparison between the two long integers. If \class{PlTerm} cannot be converted a \except{type_error} is raised. \cfunction{int}{PlTerm::operator ==}{const wchar_t *} \nodescription \cfunction{int}{PlTerm::operator ==}{const char *} Yields \const{TRUE} if the \class{PlTerm} is an atom or string representing the same text as the argument, \const{FALSE} if the conversion was successful, but the strings are not equal and an \except{type_error} exception if the conversion failed. \end{description} Below are some typical examples. See \secref{dirplatom} for direct manipulation of atoms in their internal representation. \begin{center} \begin{tabularlp}{\tt A1 == PlCompound("a(1)")} \hline \tt A1 $<$ 0 & Test \arg{A1} to hold a Prolog integer or float that can be transformed lossless to an integer less than zero. \\ \tt A1 $<$ PlTerm(0) & \arg{A1} is before the term 0' in the standard order of terms'. This means that if \arg{A1} represents an atom, this test yields \const{TRUE}. \\ \tt A1 == PlCompound("a(1)") & Test \arg{A1} to represent the term \exam{a(1)}. \\ \tt A1 == "now" & Test \arg{A1} to be an atom or string holding the text now''. \\ \hline \end{tabularlp} \end{center} \subsection{Analysing compound terms} \label{sec:cpp-plterm-compound} Compound terms can be viewed as an array of terms with a name and arity (length). This view is expressed by overloading the \const{[]} operator. A \except{type_error} is raised if the argument is not compound and a \except{domain_error} if the index is out of range. In addition, the following functions are defined: \begin{description} \cfunction{PlTerm}{PlTerm::operator []}{int arg} If the \class{PlTerm} is a compound term and \arg{arg} is between 1 and the arity of the term, return a new \class{PlTerm} representing the arg-th argument of the term. If \class{PlTerm} is not compound, a \except{type_error} is raised. Id \arg{arg} is out of range, a \except{domain_error} is raised. Please note the counting from 1 which is consistent to Prolog's arg/3 predicate, but inconsistent to C's normal view on an array. See also class \class{PlCompound}. The following example tests \arg{x} to represent a term with first-argument an atom or string equal to \exam{gnat}. \begin{code} ..., if ( x[1] == "gnat" ) ... \end{code} \cfunction{const char *}{PlTerm::name}{} Return a \type{const char *} holding the name of the functor of the compound term. Raises a \except{type_error} if the argument is not compound. \cfunction{int}{PlTerm::arity}{} Returns the arity of the compound term. Raises a \except{type_error} if the argument is not compound. \end{description} \subsection{Miscellaneous} \label{sec:cpp-plterm-misc} \begin{description} \cfunction{int}{PlTerm::type}{} Yields the actual type of the term as PL_term_type(). Return values are \const{PL_VARIABLE}, \const{PL_FLOAT}, \const{PL_INTEGER}, \const{PL_ATOM}, \const{PL_STRING} or \const{PL_TERM} \end{description} To avoid very confusing combinations of constructors and therefore possible undesirable effects a number of subclasses of \class{PlTerm} have been defined that provide constructors for creating special Prolog terms. These subclasses are defined below. \subsection{The class PlString} \label{sec:cpp-plstring} A SWI-Prolog string represents a byte-string on the global stack. It's lifetime is the same as for compound terms and other data living on the global stack. Strings are not only a compound representation of text that is garbage-collected, but as they can contain 0-bytes, they can be used to contain arbitrary C-data structures. \begin{description} \constructor{PlString}{const wchar_t *text} \nodescription \constructor{PlString}{const char *text} Create a SWI-Prolog string object from a 0-terminated C-string. The \arg{text} is copied. \constructor{PlString}{const wchar_t *text, size_t len} \nodescription \constructor{PlString}{const char *text, size_t len} Create a SWI-Prolog string object from a C-string with specified length. The \arg{text} may contain 0-characters and is copied. \end{description} \subsection{The class PlCodeList} \label{sec:cpp-codelist} \begin{description} \constructor{PlCodeList}{const wchar_t *text} \nodescription \constructor{PlCodeList}{const char *text} Create a Prolog list of ASCII codes from a 0-terminated C-string. \end{description} \subsection{The class PlCharList} \label{sec:cpp-plcharlist} Character lists are compliant to Prolog's atom_chars/2 predicate. \begin{description} \constructor{PlCharList}{const wchar_t *text} \nodescription \constructor{PlCharList}{const char *text} Create a Prolog list of one-character atoms from a 0-terminated C-string. \end{description} \subsection{The class PlCompound} \label{sec:cpp-plcompound} \begin{description} \constructor{PlCompound}{const wchar_t *text} \nodescription \constructor{PlCompound}{const char *text} Create a term by parsing (as read/1) the \arg{text}. If the \arg{text} is not valid Prolog syntax, a \except{syntax_error} exception is raised. Otherwise a new term-reference holding the parsed text is created. \constructor{PlCompound}{const wchar_t *functor, PlTermv args} \nodescription \constructor{PlCompound}{const char *functor, PlTermv args} Create a compound term with the given name from the given vector of arguments. See \class{PlTermv} for details. The example below creates the Prolog term \exam{hello(world)}. \begin{code} PlCompound("hello", PlTermv("world")) \end{code} \end{description} \subsection{The class PlTail} \label{sec:pltail} The class \class{PlTail} is both for analysing and constructing lists. It is called \class{PlTail} as enumeration-steps make the term-reference follow the tail' of the list. \begin{description} \constructor{PlTail}{PlTerm list} A \class{PlTail} is created by making a new term-reference pointing to the same object. As \class{PlTail} is used to enumerate or build a Prolog list, the initial \arg{list} term-reference keeps pointing to the head of the list. \cfunction{int}{PlTail::append}{const PlTerm \&element} Appends \arg{element} to the list and make the \class{PlTail} reference point to the new variable tail. If \arg{A} is a variable, and this function is called on it using the argument \exam{"gnat"}, a list of the form \exam{[gnat|B]} is created and the \class{PlTail} object now points to the new variable \arg{B}. This function returns \const{TRUE} if the unification succeeded and \const{FALSE} otherwise. No exceptions are generated. The example below translates the main() argument vector to Prolog and calls the prolog predicate entry/1 with it. \begin{code} int main(int argc, char **argv) { PlEngine e(argv[0]); PlTermv av(1); PlTail l(av[0]); for(int i=0; i/. This interface uses the \const{PL_FA_VARARGS} calling convention, where the argument list of the predicate is passed using an array of \type{term_t} objects as returned by PL_new_term_refs(). This interface poses no limits on the arity of the predicate and is faster, especially for a large number of arguments. \constructor{PlRegister}{const char *module, const char *name, foreign_t (*f)(PlTerm a0, \ldots)} Registers functions for use with the traditional calling conventional, where each positional argument to the predicate is passed as an argument to the function \arg{f}. This can be used to define functions as predicates similar to what is used in the C-interface: \begin{code} static foreign_t pl_hello(PlTerm a1) { ... } PlRegister x_hello_1(NULL, "hello", 1, pl_hello); \end{code} This construct is currently supported upto 3 arguments. \end{description} \section{The class PlQuery} \label{sec:cpp-plquery} This class encapsulates the call-backs onto Prolog. \begin{description} \constructor{PlQuery}{const char *name, const PlTermv \&av} Create a query where \arg{name} defines the name of the predicate and \arg{av} the argument vector. The arity is deduced from \arg{av}. The predicate is located in the Prolog module \module{user}. \constructor{PlQuery}{const char *module, const char *name, const PlTermv \&av} Same, but performs the predicate lookup in the indicated module. \cfunction{int}{PlQuery::next_solution}{} Provide the next solution to the query. Yields \const{TRUE} if successful and \const{FALSE} if there are no (more) solutions. Prolog exceptions are mapped to C++ exceptions. \end{description} Below is an example listing the currently defined Prolog modules to the terminal. \begin{code} PREDICATE(list_modules, 0) { PlTermv av(1); PlQuery q("current_module", av); while( q.next_solution() ) cout << (char *)av[0] << endl; return TRUE; } \end{code} In addition to the above, the following functions have been defined. \begin{description} \cfunction{int}{PlCall}{const char *predicate, const PlTermv \&av} Creates a \class{PlQuery} from the arguments generates the first next_solution() and destroys the query. Returns the result of next_solution() or an exception. \cfunction{int}{PlCall}{const char *module, const char *predicate, const PlTermv \&av} Same, locating the predicate in the named module. \cfunction{int}{PlCall}{const wchar_t *goal} \nodescription \cfunction{int}{PlCall}{const char *goal} Translates \arg{goal} into a term and calls this term as the other PlCall() variations. Especially suitable for simple goals such as making Prolog load a file. \end{description} \subsection{The class PlFrame} \label{sec:cpp-plframe} The class \class{PlFrame} provides an interface to discard unused term-references as well as rewinding unifications (\jargon{data-backtracking}). Reclaiming unused term-references is automatically performed after a call to a C++-defined predicate has finished and returns control to Prolog. In this scenario \class{PlFrame} is rarely of any use. This class comes into play if the toplevel program is defined in C++ and calls Prolog multiple times. Setting up arguments to a query requires term-references and using \class{PlFrame} is the only way to reclaim them. \begin{description} \constructor{PlFrame}{} Creating an instance of this class marks all term-references created afterwards to be valid only in the scope of this instance. \destructor{PlFrame} Reclaims all term-references created after constructing the instance. \cfunction{void}{PlFrame::rewind}{} Discards all term-references {\bf and} global-stack data created as well as undoing all unifications after the instance was created. \end{description} \index{assert}% A typical use for \class{PlFrame} is the definition of C++ functions that call Prolog and may be called repeatedly from C++. Consider the definition of assertWord(), adding a fact to word/1: \begin{code} void assertWord(const char *word) { PlFrame fr; PlTermv av(1); av[0] = PlCompound("word", PlTermv(word)); PlQuery q("assert", av); q.next_solution(); } \end{code} This example shows the most sensible use of \class{PlFrame} if it is used in the context of a foreign predicate. The predicate's thruth-value is the same as for the Prolog unification (=/2), but has no side effects. In Prolog one would use double negation to achieve this. \begin{code} PREDICATE(can_unify, 2) { PlFrame fr; int rval = (A1=A2); fr.rewind(); return rval; } \end{code} \section{The PREDICATE macro} \label{sec:cpp-predicate-macro} The PREDICATE macro is there to make your code look nice, taking care of the interface to the C-defined SWI-Prolog kernel as well as mapping exceptions. Using the macro \begin{code} PREDICATE(hello, 1) \end{code} is the same as writing: \begin{code} static foreign_t pl_hello__1(PlTermv PL_av); static foreign_t _pl_hello__1(term_t t0, int arity, control_t ctx) { (void)arity; (void)ctx; try { return pl_hello__1(PlTermv(1, t0)); } catch ( PlTerm &ex ) { return ex.raise(); } } static PlRegister _x_hello__1("hello", 1, _pl_hello__1); static foreign_t pl_hello__1(PlTermv PL_av) \end{code} The first function converts the parameters passed from the Prolog kernel to a \class{PlTermv} instance and maps exceptions raised in the body to Prolog exceptions. The \class{PlRegister} global constructor registers the predicate. Finally, the function header for the implementation is created. \subsection{Variations of the PREDICATE macro} \label{sec:cpp-predicate-macro-variations} The PREDICATE() macros has a number of variations that deal with special cases. \begin{description} \cmacro{}{PREDICATE0}{name} This is the same as PREDICATE(name, 0). It avoids a compiler warning about that \const{PL_av} is not used. \cmacro{}{NAMED_PREDICATE}{plname, cname, arity} This version can be used to create predicates whose name is not a valid C++ identifier. Here is a ---hypothetical--- example, which unifies the second argument with a stringified version of the first. The cname' is used to create a name for the functions. The concrete name does not matter, but must be unique. Typically it is a descriptive name using the limitations imposed by C++ indentifiers. \begin{code} NAMED_PREDICATE("#", hash, 2) { A2 = (wchar_t*)A1; } \end{code} \cmacro{}{NAMED_PREDICATE_NONDET}{plname, cname, arity} Define a non-deterministic Prolog predicate in C++. See \file{SWI-cpp.h}. FIXME: Needs cleanup and an example. \end{description} \subsection{Controlling the Prolog destination module} \label{sec:cpp-module} With no special precautions, the predicates are defined into the module from which load_foreign_library/1 was called, or in the module \const{user} if there is no Prolog context from which to deduce the module such as while linking the extension statically with the Prolog kernel. Alternatively, {\em before} loading the SWI-Prolog include file, the macro PROLOG_MODULE may be defined to a string containing the name of the destination module. A module name may only contain alpha-numerical characters (letters, digits, _). See the example below: \begin{code} #define PROLOG_MODULE "math" #include #include PREDICATE(pi, 1) { A1 = M_PI; } \end{code} \begin{code} ?- math:pi(X). X = 3.14159 \end{code} \section{Exceptions} \label{sec:cpp-exceptions} Prolog exceptions are mapped to C++ exceptions using the subclass \class{PlException} of \class{PlTerm} to represent the Prolog exception term. All type-conversion functions of the interface raise Prolog-compliant exceptions, providing decent error-handling support at no extra work for the programmer. For some commonly used exceptions, subclasses of \class{PlException} have been created to exploit both their constructors for easy creation of these exceptions as well as selective trapping in C++. Currently, these are \class{PlTypeEror} and \class{PlDomainError}. To throw an exception, create an instance of \class{PlException} and use throw() or PlException::cppThrow(). The latter refines the C++ exception class according to the represented Prolog exception before calling throw(). \begin{code} char *data = "users"; throw PlException(PlCompound("no_database", PlTerm(data))); \end{code} \subsection{The class PlException} \label{sec:cpp-plexception} This subclass of \class{PlTerm} is used to represent exceptions. Currently defined methods are: \begin{description} \constructor{PlException}{const PlTerm \&t} Create an exception from a general Prolog term. This is provides the interface for throwing any Prolog terms as an exception. \cppcast{PlException}{wchar_t *} \nodescription \cppcast{PlException}{char *} The exception is translated into a message as produced by print_message/2. The character data is stored in a ring. Example: \begin{code} ...; try { PlCall("consult(load)"); } catch ( PlException &ex ) { cerr << (char *) ex << endl; } \end{code} \cfunction{int}{plThrow}{} Used in the PREDICATE() wrapper to pass the exception to Prolog. See PL_raise_exeption(). \cfunction{int}{cppThrow}{} Used by PlQuery::next_solution() to refine a generic \class{PlException} representing a specific class of Prolog exceptions to the corresponding C++ exception class and finally then executes throw(). Thus, if a \class{PlException} represents the term \begin{quote} \term{error}{\term{type_error}{Expected, Actual}, Context} \end{quote} PlException::cppThrow() throws a \class{PlTypeEror} exception. This ensures consistency in the exception-class whether the exception is generated by the C++-interface or returned by Prolog. The following example illustrates this behaviour: \begin{code} PREDICATE(call_atom, 1) { try { return PlCall((char *)A1); } catch ( PlTypeError &ex ) { cerr << "Type Error caugth in C++" << endl; cerr << "Message: \"" << (char *)ex << "\"" << endl; return FALSE; } } \end{code} \end{description} \subsection{The class PlTypeError} \label{sec:cpp-pl-type-error} A \jargon{type error} expresses that a term does not satisfy the expected basic Prolog type. \begin{description} \constructor{PlTypeError}{const char *expected, const PlTerm \&actual} Creates an ISO standard Prolog error term expressing the \arg{expected} type and \arg{actual} term that does not satisfy this type. \end{description} \subsection{The class PlDomainError} \label{sec:cpp-pl-domain-error} A \jargon{domain error} expresses that a term satisfies the basic Prolog type expected, but is unacceptable to the restricted domain expected by some operation. For example, the standard Prolog open/3 call expect an \const{io_mode} (read, write, append, ...). If an integer is provided, this is a \jargon{type error}, if an atom other than one of the defined io-modes is provided it is a \jargon{domain error}. \begin{description} \constructor{PlDomainError}{const char *expected, const PlTerm \&actual} Creates an ISO standard Prolog error term expressing a the \arg{expected} domain and the \arg{actual} term found. \end{description} \section{Embedded applications} \label{sec:cpp-embedding} Most of the above assumes Prolog is in charge' of the application and C++ is used to add functionality to Prolog, either for accessing external resources or for performance reasons. In some applications, there is a \jargon{main-program} and we want to use Prolog as a \jargon{logic server}. For these applications, the class \class{PlEngine} has been defined. Only a single instance of this class can exist in a process. When used in a multi-threading application, only one thread at a time may have a running query on this engine. Applications should ensure this using proper locking techniques.% \footnote{For Unix, there is a multi-threaded version of SWI-Prolog. In this version each thread can create and destroy a thread-engine. There is currently no C++ interface defined to access this functionality, though ---of course--- you can use the C-functions.} \begin{description} \constructor{PlEngine}{int argc, char **argv} Initialises the Prolog engine. The application should make sure to pass \exam{argv[0]} from its main function, which is needed in the Unix version to find the running executable. See PL_initialise() for details. \constructor{PlEngine}{char *argv0} Simple constructure using the main constructor with the specified argument for \exam{argv[0]}. \destructor{PlEngine} Calls PL_cleanup() to destroy all data created by the Prolog engine. \end{description} \Secref{pltail} has a simple example using this class. \section{Considerations} \label{sec:cpp-considerations} \subsection{The C++ versus the C interface} \label{sec:cpp-vs-c} Not all functionality of the C-interface is provided, but as \class{PlTerm} and \type{term_t} are essentially the same thing with automatic type-conversion between the two, this interface can be freely mixed with the functions defined for plain C. Using this interface rather than the plain C-interface requires a little more resources. More term-references are wasted (but reclaimed on return to Prolog or using \class{PlFrame}). Use of some intermediate types (\type{functor_t} etc.) is not supported in the current interface, causing more hash-table lookups. This could be fixed, at the price of slighly complicating the interface. \subsection{Static linking and embedding} \label{sec:cpp-linking} The mechanisms outlined in this document can be used for static linking with the SWI-Prolog kernel using \manref{swipl-ld}{1}. In general the C++ linker should be used to deal with the C++ runtime libraries and global constructors. \subsection{Status and compiler versions} \label{sec:cpp-status} The current interface is entirely defined in the \fileext{h} file using inlined code. This approach has a few advantages: as no C++ code is in the Prolog kernel, different C++ compilers with different name-mangling schemas can cooperate smoothly. Also, changes to the header file have no consequences to binary compatibility with the SWI-Prolog kernel. This makes it possible to have different versions of the header file with few compatibility consequences. \section{Conclusions} \label{sec:conclusions} \label{sec:cpp-conclusions} In this document, we presented a high-level interface to Prolog exploiting automatic type-conversion and exception-handling defined in C++. Programming using this interface is much more natural and requires only little extra resources in terms of time and memory. Especially the smooth integration between C++ and Prolog exceptions reduce the coding effort for type checking and reporting in foreign predicates. \printindex \end{document}