I’m new to prolog in C++ (I’ve used the python library before). I have tried to read the documentation, but am pretty lost. I have several questions about the installation and usage of the C++ interface.
(1) How to install swi-prolog? Is there a complete documentation for this?
(2) Is there a simple running example of how to use the prolog interface in C++?
Which OS?
Depends on the OS. For Windows, Mac and Ubuntu it is a common install.
This is still pretty much a work in progress. The best way to keep up on it is to follow the pull request on GitHub.
The “new” C++ API is SWI-cpp2.h
is no longer a “work in progress” but is finished. It’s documented here – this might not yet have been refreshed to the very latest, and I need to proof-read it.
Simple examples are in the documentation and also in the files test_cpp.cpp and test_cpp.pl. As @EricGT mentioned, the rocksdb pack is a more complex example: Use more C++ features by kamahen · Pull Request #18 · JanWielemaker/rocksdb · GitHub (I’ve mostly finished modifying the code into a more “natural” C++ style; and I’ve also added some features to the library in the process0>
For Prolog-calling-C++, almost every piece of C functionality has a corresponding piece of C++ functionality. In addition, the C++ API extends the C API by handling C++ exceptions, and by taking advantage of C++'s RAII (Resource Acquisition Is Initialization) to simplify error handling and cleanup. For example, the C++ code for non-deterministic predicates is considerably simpler than the equivalent C code; and the new “blob” interface tries to have suitable defaults for almost everything, so that a “blob” can be defined by just a few lines of boilerplate.
For C++ calling Prolog, the story is a bit less complete, mainly because I don’t currently have much need for it. The basics work fine, but if you need some of the more advanced capabilities, you might need to drop into C. If that’s the case, please open an issue against GitHub - SWI-Prolog/packages-cpp: The SWI-Prolog C++ interface
The discussion around my changes to the C++ API are mostly here: Changes to SWI-cpp.h
I’ve started to proof-read the C++ documentation (@jan just pushed SWI-Prolog 9.1.4 with my latest changes) and I realize that the documentation for failure, errors, and exceptions is quite confusing – the design changed considerably while I was working on it (incorporating feedback from users) and the documentation has a lot of out-of-date stuff in it.
For now, test_cpp.cpp
and rocksdb4pl.cpp
are the best examples; I’ll try to fix the documentation in the next week or two – but I’m not a professional tech writer. If you have questions, please ask in this forum or DM me.
Thanks for your reply. But the example you gave didn’t seem to answer my confusion. Basically, I want to know how the following python functions can be done in C++.
- prolog.consult(‘example.pl’)
- prolog.query
- prolog.assertz
- prolog.retract
Is there an example containing these mappings?
You can find some examples in https://github.com/SWI-Prolog/packages-cpp/blob/97d5ed02656034f64fa47f54a8ed86289a5f043e/test_cpp.cpp , by searching for Query
. For example:
PREDICATE(hello, 0)
{ PlQuery q("write", PlTermv(PlTerm_atom("hello hello hello")));
PlCheckFail(q.next_solution());
return true;
}
can be easily changed to be your consult('example.pl')
.
These require being called from Prolog (they’re predicates); if you want to call from C++, look at this section of the manual: SWI-Prolog -- Calling Prolog from C … there are some thin C++ wrappers for this (e.g., PlPredicate
for predicate_t
, Plx_pred()
, Plx_open_query()
(the latter is wrapped in class PlQuery
).
I should add some examples and test cases, I suppose; but that would require a bit of work because the current test setup is Prolog calling C/C++. There’s also some C code in test_ffi.c
(search for PL_open_query
… but this is designed for testing some documentation details and doesn’t make great reading.
Sorry, I’m confused. I can’t find a “full” small running example in the code, most of the examples in the documentation are code snippets. For example, I have the following prolog file example.pl
"
fact(1).
fact(2).
fact(3).
"
I followed the documentation SWI-Prolog -- The class PlQuery (version 2)
And if I write the following code.
#include <bits/stdc++.h>
using std::cout;
using std::cin;
using std::endl;
#include <SWI-Prolog.h>
#include <SWI-Stream.h>
#include <SWI-cpp.h>
int main(int argc, char **argv)
{
PL_initialise(argc, argv);
PlCall("consult('example.pl')");
PlTermv av(1);
PlQuery q("fact", av);
while( q.next_solution() )
cout << av[0].as_string() << endl;
return 0;
}
I would get a compile error
error: ‘class PlTerm’ has no member named ‘as_string’
#include <SWI-cpp.h>
SWI-cpp2.h
corresponds to the “version 2” documentation at SWI-Prolog -- A C++ interface to SWI-Prolog (Version 2)
You also need to include SWI-cpp2.cpp
(this probably isn’t documented well - sorry).
Thanks a lot, it helps. I have another question related to assertz. What’s the proper way of calling assertz? When I assertz a fact into the prolog knowledge base, the permission error would appear. The example.pl contains fact(1)…fact(4).
When I tried to insert fact(5) into the knowledge base, the error appears.
int main(int argc, char **argv)
{
PL_initialise(argc, argv);
PlCall("consult('example.pl')");
term_t assertz_query = PL_new_term_ref();
PL_chars_to_term("assertz(fact(5))", assertz_query);
PL_call(assertz_query, NULL);
PlTermv av(1);
PlQuery q("fact", av);
try {
while( q.next_solution() ) {
auto res = (char *) av[0];
int val = atoi(res);
std::cout << ((char *)av[0]) << std::endl;
}
} catch ( PlException &ex ) {
std::cerr << (char *) ex << std::endl;
}
PL_cleanup(1);
return 0;
}
1
2
3
4
error(permission_error(modify,static_procedure,fact/1),context(system:assertz/1,_))
You can easily reproduce the error without C++, which feels useless but it shows that the problem is elsewhere:
$ echo 'fact(1).' > foo.pl
$ swipl foo.pl
Welcome to SWI-Prolog (threaded, 64 bits, version 9.1.13)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?- assertz(fact(2)).
ERROR: No permission to modify static procedure `fact/1'
You could define fact/1 as dynamic:
$ cat foo.pl
:- dynamic fact/1.
fact(1).
$ swipl foo.pl
Welcome to SWI-Prolog (threaded, 64 bits, version 9.1.13)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?- assertz(fact(2)).
true.
?- fact(X).
X = 1 ;
X = 2.
I should hope this works also from C++
Thanks a lot, it helps.
I’ve noticed that the likes.cpp
example code doesn’t compile. Here’s a quick fix to the code: FIXED: likes.cpp sample code by kamahen · Pull Request #51 · SWI-Prolog/packages-cpp · GitHub
A few comments on your code:
- Most of the
PL_*()
functions have an equivalent C++ API. E.g.,instead ofPL_unify_int64()
, you can usePlTerm::unify_integer()
. - If you’re going to use the
PL_*()
functions, I suggest using the relatedPlx_*()
functions instead. As appropriate, these check the return code and throw a C++ exception if there’s an error. E.g., instead ofPL_call(assertz_query, NULL)
, I suggestPlx_call(assertz_query, nullptr)
. The documentation for thePlx_*()
functions is a bit scanty, partly because in most cases, they don’t need to be called directly. - The
char*
casts are deprecated (you should have got compiler warnings), and replaced by theas_string()
methods. Also,(char*)
is a C-style cast; for C++, it’s recommended to usestatic_cast<char*>(...)
. - Use the
PlEngine
class instead of callingPL_initialise()
andPL_cleanup()
(orPlx_initialise()
,Plx_cleanup()
). - Instead of
term_t assertz_query = Pl_new_term_ref()
, you can declare it withPlTerm_var assertz_query
. - I see that there’s no C++ equivalent of
PL_chars_to_term()
– I’ll add that. In the meantime, if you defineassertz_query
the way I suggested, you can callPlx_chars_to_term("assertz(fact(5))", assertz_query.C_)
. atoi()
has been deprecated for many years – usestrtol()
instead.- Instead of catching
PlException
, catchPlExceptionBase
and usewhat()
to get the exception as a string.
Thanks for your reply. I modified the code to version 2. However, there’s a compile error that I don’t know how to fix. Could you please tell me what’s wrong with the code? I think I just followed the version 2 documentation for this. Seems like av[0] is not allowed?
#include <bits/stdc++.h>
#include <SWI-Prolog.h>
#include <SWI-cpp2.h>
void run_query() {
PlTermv av(1);
PlQuery q("hello", av);
try {
while( q.next_solution() ) {
auto res = av[0].as_string();
std::cout << res << std::endl;
int val = atoi(res.c_str());
}
} catch ( PlExceptionBase &ex ) {
std::cerr << ex.what() << std::endl;
}
}
// compile command: g++ -std=c++17 trial.cpp -o trial -L/usr/lib/swi-prolog/lib/x86_64-linux -lswipl
int main(int argc, char **argv)
{
Plx_initialise(argc, argv);
PlCall("consult('example.pl')");
PlCall("assertz(fact(1))");
PlCall("assertz(fact(2))");
PlCall("assertz(fact(5))");
run_query();
Plx_cleanup(1);
return 0;
}
The compile error was:
/usr/bin/ld: /tmp/ccGVae1L.o: in function `run_query()':
trial.cpp:(.text+0x9f): undefined reference to `PlTermv::operator[](unsigned long) const'
/usr/bin/ld: trial.cpp:(.text+0xb7): undefined reference to `PlTerm::as_string[abi:cxx11](PlEncoding) const'
/usr/bin/ld: trial.cpp:(.text+0x11a): undefined reference to `PlQuery::next_solution()'
/usr/bin/ld: /tmp/ccGVae1L.o: in function `main':
trial.cpp:(.text+0x2b6): undefined reference to `PlCall(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
/usr/bin/ld: trial.cpp:(.text+0x305): undefined reference to `PlCall(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
/usr/bin/ld: trial.cpp:(.text+0x354): undefined reference to `PlCall(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
/usr/bin/ld: trial.cpp:(.text+0x3a3): undefined reference to `PlCall(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)'
/usr/bin/ld: /tmp/ccGVae1L.o: in function `void PlEx<int>(int, __PL_queryRef*)':
trial.cpp:(.text._Z4PlExIiEvT_P13__PL_queryRef[_Z4PlExIiEvT_P13__PL_queryRef]+0x21): undefined reference to `PlEx_fail(__PL_queryRef*)'
/usr/bin/ld: /tmp/ccGVae1L.o: in function `unsigned long PlWrap<unsigned long>(unsigned long, __PL_queryRef*)':
trial.cpp:(.text._Z6PlWrapImET_S0_P13__PL_queryRef[_Z6PlWrapImET_S0_P13__PL_queryRef]+0x23): undefined reference to `PlWrap_fail(__PL_queryRef*)'
/usr/bin/ld: /tmp/ccGVae1L.o: in function `__PL_procedure* PlWrap<__PL_procedure*>(__PL_procedure*, __PL_queryRef*)':
trial.cpp:(.text._Z6PlWrapIP14__PL_procedureET_S2_P13__PL_queryRef[_Z6PlWrapIP14__PL_procedureET_S2_P13__PL_queryRef]+0x23): undefined reference to `PlWrap_fail(__PL_queryRef*)'
/usr/bin/ld: /tmp/ccGVae1L.o: in function `__PL_queryRef* PlWrap<__PL_queryRef*>(__PL_queryRef*, __PL_queryRef*)':
trial.cpp:(.text._Z6PlWrapIP13__PL_queryRefET_S2_S1_[_Z6PlWrapIP13__PL_queryRefET_S2_S1_]+0x23): undefined reference to `PlWrap_fail(__PL_queryRef*)'
/usr/bin/ld: /tmp/ccGVae1L.o: in function `void PlEx<bool>(bool, __PL_queryRef*)':
trial.cpp:(.text._Z4PlExIbEvT_P13__PL_queryRef[_Z4PlExIbEvT_P13__PL_queryRef]+0x25): undefined reference to `PlEx_fail(__PL_queryRef*)'
collect2: error: ld returned 1 exit status
You need to add this line:
#include <SWI-cpp2.cpp>
I suggest using something like the following command to compile and link (which will pick up the libraries and also pick up example.pl
(which means you won’t need to do the call to consult(example.pl)
):
swipl-ld -goal true -o trial -ld g++ -g -O trial.cpp example.pl
little side note:
to combine the C++ STL Standard Containers, you do not need to code:
using std::cout;
…
A simple one liner is enough, to avaid the std::
int the CodeLine: std::cout << “Hello” << std::endl;
if you set a line like this;
#using namespace std;
this using Command includes all std:: Containers (vector, map, cout …).
Happy coding …
using namespace std;
I know that this is common, but the Google C++ style guide frowns upon it and I mostly agree with that style guide. Although, to be fair, it’s for code bases with many millions lines of source code.
I know that IDEs can sort this out, but often I read code without the benefit of an IDE (e.g., I’m looking at something in Github).
The Google Python guide also discourages from foo import *
for a similar reason, and that seems to be a common opinion in the Python community.
I like to be explicit when I import from Prolog modules also, but that’s a bit more difficult to do because Prolog doesn’t usually use the module:predicate(...)
style in code.
I use github.com, too.
My actual Project is a mix of some Languages - at current Time, I try me on Prolog implementation.
You can follow the Process there:
privateEditor
it is a one men Project, yet.
But it is target on my fun in coding.
If you have Question or Feedback, drop e message.