SWI-Prolog / packages-cpp

The SWI-Prolog C++ interface
29 stars 13 forks source link

How to write a predicate with the PREDICATE macro that supports multiple bindings for one variable? #4

Open airballking opened 6 years ago

airballking commented 6 years ago

I have a beginner question about the SWI-Prolog C++ interface. If this is the wrong place to post such a question, I apologize and ask: Where should I ask such a question?

I want to create a predicate foo_bar/1 that binds two possible solutions to its variable. Meaning, I want the following behavior of the resulting call in prolog:

?- foo_bar(X).
X = foo ;
X = bar.

In Prolog, I'd implement it like this:

foo_bar(X) :-
  X = foo;
  X = bar.

Now, I'd like to implement the same behavior using the C++ interface. I tried this:

PREDICATE(foo_bar, 1) {
    PL_A1 = std::string("foo").c_str();
    PL_A1 = std::string("bar").c_str();
    return true;
}

But it only returns the first binding:

?- foo(X).
X = foo.

Does the C++ interface support implementing this type of predicate? If yes, how can I do it?

Thank you very much in advance for your support!

wouterbeek commented 6 years ago

I learned how to write these by looking at some examples, e.g., the following predicate by Jan is non-deterministic: https://github.com/JanWielemaker/hdt/blob/e0a0eff87fc3318434cb493690c570e1255ed30e/c/hdt4pl.cpp#L363

vmatare commented 6 years ago

Seems like it's not explicitly supported by the C++ binding. But since it's just a wrapper around the C foreign language interface, you can just use that.

airballking commented 6 years ago

@wouterbeek and @vmatare thank you for the quick and insightful reponses! The combined information on both linked pages gave me an idea of how to implement such a predicate.

However, when I started implementing my predicate, the compiler very quickly complained about an invalid type cast in the SWI-PL infrastructure. Here is a minimal version of what I tried that still produces the error:

PREDICATE_NONDET(foo_bar, 1) {
    switch( PL_foreign_control(handle) ) {}
}

Here is the error that I got:

error: invalid conversion from ‘foreign_t {aka long unsigned int}’ to ‘control_t {aka void*}’ [-fpermissive]
     switch( PL_foreign_control(handle) ) {
                                      ^
In file included from /usr/lib/swi-prolog/include/SWI-cpp.h:27:0,
                 from /home/georg/ros/knowrob/src/knowrob/rosprolog/include/rosprolog.h:8,
                 from /home/georg/ros/knowrob/src/knowrob/knowrob_srdl/src/urdf_parsing.cpp:1:
/usr/lib/swi-prolog/include/SWI-Prolog.h:274:17: note:   initializing argument 1 of ‘int PL_foreign_control(control_t)’
 PL_EXPORT(int)  PL_foreign_control(control_t);

It seems that the variable handle, as provided by PREDICATE_NONDET, is not of a type directly compatible with what PL_foreign_control() expects.

Do you see any obvious mistake I made? Or, do you have an idea how to fix this?

wouterbeek commented 6 years ago

I don't understand your error, line 27 in my SWI-cpp.h file falls within a multi-line comment.

airballking commented 6 years ago

I read this hint as: At line 274 of SWI-Prolog.h, which is included from SWI-cpp.h at line 27, which is included from ..., you can find the definition of the failing PL_foreign_control(control_t).

JanWielemaker commented 6 years ago

But, indeed line 27 is in a comment, so you probably have an old version. At least use version 7.6.x

airballking commented 6 years ago

Ah, I see. Sorry for the confusion. I am using SWI-Prolog from deb packages on ROS 16.04. apt-cache says that is version 7.2.3-2. I'll try installing a newer version.

wouterbeek commented 6 years ago

@airballking The Linux packages are very outdated, unfortunately. Luckily, SWI-Prolog is very easy to compile on Linux. The Debian-specific instructions are documented here: http://www.swi-prolog.org/build/Debian.html

airballking commented 6 years ago

@wouterbeek thank you for the link!