We start with a simple program shown, in Figure 3.1, that will call the following XSB predicate
p(a,b,c). p(1,2,3). p([1,2],[3,4],[5,6]). p(A,B,A). r(c,b,a). r(3,2,1). r([5,6],[3,4],[1,2]). r(_A,B,B).and backtrack through unifying answers (cf. $XSBDIR/examples/c_calling_xsb/edb.P). . This example will only compile properly if the sequential engine is used, and its style is not recommended: it will be shown in Section 3.2.2 how to extend the style.
We discuss the program in Figure 3.1 in detail. This program, slightly modified so that it compiles with the multi-threaded engine is in $XSBDIR/examples/c_calling_xsb/cvartest.c. An executable for this program can be make most easily by calling $XSBDIR/examples/c_calling_xsb/make.P, which makes the executable cvstest.
The program begins by including some standard C headers: note that string.h is needed for string manipulation routines such as strcpy. In addition, the XSB library header cinterf.h is necessary for the XSB C API. Since the program in Figure 3.1 uses functions in the VarString interface, within main() the routine XSB_StrDefine(return_string) declares and initializes a structure of type VarString, named return_string.
The next order of business is to initialize XSB. In order to do this, xsb_init_string() needs to know the installation directory for XSB, which must be passed as part of the initialization string. In Figure 3.1 this is done by manipulating the path of the executable (cvstest) that calls XSB. In fact any other approach would also work as long as the XSB installation directory were passed. Within the initialization string, other command line arguments can be passed to XSB if desired with the following exceptions: the arguments -B (boot module), -D (command loop driver), -i (interpreter) and -d (disassembler) cannot be used when calling XSB from a foreign language 3.2. As a final point on initialization, note that the function xsb_init() can also be used to initialize XSB based on an argument vector and count (see Section 3.3).
Note that the calling program checks for any errors returned by xsb_init_string() and other API commands. In general, xsb_init_string() may throw an error if the XSB's installation directory has become corrupted, or for similar reasons. This mechanism for error handling is different than that used if XSB is called in its usual stand-alone mode, in which case such an error would cause XSB to exit). An error returned by XSB's API are similar to an error ball described in Volume 1 Exception Handling in that it has both a type and a message. For normal Prolog exceptions, XSB's API will throw the same kinds of errors as XSB called in a stand-alone (or server) mode, i.e. instantiation errors, type errors, etc. However XSB's API adds two new error types:
and a string pointer to the associated message can be found by the function xsb_get_init_error_message().
As can be seen from the example, handling errors from commands is done in manner similar to that of initialization. For non-initialization errors, a string pointer to the type can be obtained by xsb_get_error_type(), while a string pointer to the message can be obtained by xsb_get_error_message().
Next in Figure 3.1 the file edb.P is consulted (containing the p/3 and r/3 predicates shown above). Note, that the argument to xsb_command_string must be a syntactically valid Prolog term ending with a period, otherwise a syntax error will be thrown, which may be displayed through xsb_get_error_type() and xsb_get_error_message() 3.3.
Queries to XSB are a little more complicated than commands. Since a query may return multiple solutions, a query should usually be called from inside a loop. In Figure 3.1, the query is opened with xsb_query_string(). If the query has at least one answer, xsb_query_string() will return XSB_SUCCESS; if the query fails, it will return XSB_FAILURE, and if there is an exception it will return XSB_ERROR as usual. Any answer will be returned as a string in the VarString return_string, and each argument of the query will be separated by the character |. Thus, in our example, the first answer will write the string
a|b|cOnce a query has been opened, subsequent answers can be obtained via xsb_next_string(). These answers are written to return_string in the same manner as xsb_query_string_string().
1|2|3 [1,2]|[3,4]|[5,6] _h102|_h116|_h102A query is automatically closed when no more answers can be derived from it. Alternately, a query that may have answers remaining can be closed using the command xsb_close_query(). If the calling application will need to pass more queries or commands to XSB nothing need be done at this point: a new queries or commands can be invoked using one of the functions just discussed. However if the calling process is finished with XSB and will never need it again during the life of the process, it can call xsb_close().