PL_get_integer returns false even with confirmed integer term_t

I’m using: SWI-Prolog version 9.3.18 for x64-win64

I want the code to: convert a prolog integer term to a C integer using PL_get_integer

But what I’m getting is: false, even though I confirm the prolog term is indeed integer

My C code in simple_prolog_c_test.c looks like this:

#include <SWI-Prolog.h>
#include <stdbool.h>
#include <stdio.h>

static foreign_t pl_mytest(term_t vector3_pl) {
	term_t x_pl = PL_new_term_ref();
	int x_c;
	
	if (!PL_get_arg(1, vector3_pl, x_pl))
		printf("could not extract x_pl\n");
	else printf("extracted x_pl\n");
	
	printf("x_pl is int? %d\n", PL_is_integer(x_pl));
	
	if (!PL_get_integer(x_pl, &x_c))
		printf("value of x is %d\n", x_c);
	else printf("could not get x_pl into x_c\n");
	return true;
}

install_t
install_simple_prolog_c_test(void)
{ 
PL_register_foreign("mytest", 1, pl_mytest, 0);
}

which I compile to a .dll with

PS C:\Users\***> & 'C:\Program Files\swipl\bin\swipl-ld.exe' -shared -o simple_prolog_c_test.dll  simple_prolog_c_test.c

My prolog interface to the C code is defined in simple_prolog_c_test.pro: [EDIT: sorry, I had the wrong file here before, thanks Peter for picking up on this.]

:- module(simple_prolog_c_test, [
	mytest/1
	]).
:- use_foreign_library(foreign(simple_prolog_c_test)).

And then my swipl session showing PL_get_integer returns false is this:

PS C:\Users\***> swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 9.3.18)
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).

1 ?-  ['simple_prolog_c_test.pro'].
true.

2 ?-  mytest(myvector(1, 2, 3)).
extracted x_pl
x_pl is int? 1
could not get x_pl into x_c
true.

3 ?- halt.
PS C:\Users\***>

Thanks for your attention!

This code is wrong:

	if (!PL_get_integer(x_pl, &x_c))
		printf("value of x is %d\n", x_c);

the ! shouldn’t be there – the PL_*() functions return a true if they succeed (unlike Unix system calls that return 0 if they succeed).

You might find it easier to use the C++ API, which takes care of most of the error checking:
simple_prolog_c_test.cpp:

#include <SWI-cpp2.h>
#include <cstdio>

PREDICATE(mytest, 1) {
  PlTerm_var x_pl;
  int x_c = A1[1].as_int();
  printf("extracted x_pl: %d\n", x_c);
  return true;
}
$ swipl-ld -shared -o simple_prolog_c_test simple_prolog_c_test.cpp
$ swipl -p foreign=. simple_prolog_c_test.pl 
Welcome to SWI-Prolog (threaded, 64 bits, version 9.3.18-48-g822fa5166-DIRTY)
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).

?- mytest(myvector(1,2,3)).
extracted x_pl: 1
true.

?- mytest(myvector(a,2,3)).
ERROR: Type error: `integer' expected, found `a' (an atom)
ERROR: In:
ERROR:   [12] mytest(myvector(a,2,3))
ERROR:   [11] toplevel_call(user:user: ...) at /home/peter/.local/lib/swipl/boot/toplevel.pl:1319

BTW, there are a few typos in your code. This is what I used to test on Linux (and which worked):
simple_prolog_c_test.c:

#include <SWI-Prolog.h>
#include <stdbool.h>
#include <stdio.h>

static foreign_t pl_mytest(term_t vector3_pl) {
	term_t x_pl = PL_new_term_ref();
	int x_c = -666;
	
	if (!PL_get_arg(1, vector3_pl, x_pl))
		printf("could not extract x_pl\n");
	else printf("extracted x_pl\n");
	
	printf("x_pl is int? %d\n", PL_is_integer(x_pl));
	
	if (PL_get_integer(x_pl, &x_c))
		printf("value of x is %d\n", x_c);
	else printf("could not get x_pl into x_c\n");
	return true;
}

install_t
install_simple_prolog_c_test(void)
{ 
PL_register_foreign("mytest", 1, pl_mytest, 0);
}

simple_prolog_c_test.pl

:- module(prolog_c_testing, [
	mytest/1
	]).

:- use_foreign_library(foreign(simple_prolog_c_test)).
$ swipl-ld -shared -o simple_prolog_c_test simple_prolog_c_test.c 
$ swipl -p foreign=. simple_prolog_c_test.pl 
Welcome to SWI-Prolog (threaded, 64 bits, version 9.3.18-48-g822fa5166-DIRTY)
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).

?- mytest(myvector(1,2,3)).
extracted x_pl
x_pl is int? 1
value of x is 1
true.

?- mytest(myvector(a,2,3)).
extracted x_pl
x_pl is int? 0
could not get x_pl into x_c
true.
3 Likes

Hi Peter,
Thank you very much! Thanks to your help I was able to get it working. Ultimately I was even able to use my original code working, even with the ! (I retried this because I suspected it should work based on the PL doc of PL_get_integer), and I apologize as it turns out I had been compiling a different test file (been hacking away on something and have a few). But that is very handy about the cpp interface, thank you, I’m still new to C and C++ generally speaking.

Thank you very much again.
-Robert

BTW, you can use Sdprintf() instead of printf() – this is preferred because it plays nicely with the SWI-prolog streams. (The declarations are in SWI-Stream.h, which SWI-cpp2.h imports for you.)

Also, note that A1[i] will throw a Prolog error if the 1st arg isn’t a compound or if i is outside its arity. If you use the C interface, you need to explicitly check the return code and return false (which will result in a Prolog error if PL_get_arg() returned false), so the code should have been something like this:

if (!PL_get_arg(1, vector3_pl, x_pl))
  return false;

The C++ API has a Plx_get_arg() that does the error checking; so if something is missing from the C++ API, you can use the underlying C API without the tedious error checking code.

1 Like

The PL_get_arg() API returns false, but never raises a Prolog error. Possibly we should add a PL_get_arg_ex(). The fact that I never did indicates that at least in my code the function is almost always called after I verified the term and its arity.
As the functor and arity of a compound is often considered a type, one typically raises a type error with some application defined type if this is not the case, i.e.

if ( !PL_is_functor(t, FUNCTOR_foo1) )
   return PL_type_error("foo", t);
...
if ( !PL_get_arg(1, t, a) )
  return false
...

Thanks. I didn’t use this because SWI-Stream.h importing windows.h and that leads to naming conflicts with another library I am importing. Of course, in C++ you can assign SWI-Stream.h and the other library their own namespaces, but I do have reasons for trying to do this project I’m working on in C if possible. I’ve read some people suggest other solutions for stopping naming conflicts, if I were more experienced with how encapsulation using header files works in C maybe I could implement them, but so far I have been able to get by without needing SWI-Stream.h.

The C++ API does the following pseudo-code (testing for compound rather than functor, and doing this type check only if PL_get_arg() fails):

  if ( PL_get_arg(index, t, result) )
    return t;
  if (!PL_compound(t) )
    /* raise type error */

Hmm. This is there to allow C to get access to the underlying Windows HANDLE objects associated with (notably) socket streams. We need a couple of constants and the HANDLE type. I guess we can use WIN32_LEAN_AND_MEAN (you can try by defining this before loading SWI-Stream.h).

We could also use some #define to in/exclude the few affected APIs. For example, leave them out and if you need them you need to do

#define SWIPL_WINDOWS_NATIVE_ACCESS 1
#include <SWI-Stream.h>

Would that be a good idea? Better name?

For context, what I am working on is prolog bindings for the raylib library. Where raylib.h conflicts with windows.h is Rectangle, CloseWindow, ShowCursor, LoadImage, DrawText, DrawTextEx, PlaySound.

Two things on this, 1. defining WIN32_LEAN_AND_MEAN did not resolve the above conflicts, however 2. Just to test SWI-streams.h, I compiled the following C code to a dll and it compiled without error: “windows-swi-streams-name-conflicts.c”

#include <SWI-Prolog.h>
#define WIN32_LEAN_AND_MEAN
	#include <SWI-Stream.h>
#undef WIN32_LEAN_AND_MEAN
#include <stdbool.h>
#include <stdio.h>

int main() {
	printf("hello world\n");
	return 0;
	}

compiled with

 & 'C:\Program Files\swipl\bin\swipl-ld.exe' -o windows-swi-streams-name-conflicts.dll -v .\windows-swi-streams-name-conflicts.c

As for if using WIN32_LEAN_AND_MEAN is a good idea, I’m no expert but the Windows Developer Center states:

Define WIN32_LEAN_AND_MEAN to exclude APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.

Which sounds relevant to me since you mentioned windows sockets.

As for

I am not experienced enough with windows.h to give anything beyond generic advice, however I will mention that in my specific case at least that I have been able to compile my bindings project so far without error by doing the following:

#define Rectangle WinRectangle
#define CloseWindow WinCloseWindow
#define ShowCursor WinShowCursor
#define LoadImage WinLoadImage
#define DrawText WinDrawText
#define DrawTextEx WinDrawTextEx
#define PlaySound WinPlaySound
	#include <SWI-Stream.h>
#undef Rectangle
#undef CloseWindow
#undef ShowCursor
#undef LoadImage
#undef DrawText
#undef DrawTextEx
#undef PlaySound

In the past day I realized I will want to use SWI-Stream.h for IOSTREAMs so I can start using atoms as blobs. However I haven’t started actually using them yet so I will update in the next few weeks if this solution has resolved everything or not.

Thanks for the details. I looked at the stuff and as of 9.3.19 (just released), you need #define SWIPL_WINDOWS_NATIVE_ACCESS 1 to make it include <windows.h> and <winsock2.h> and make the API available to get access to the HANDLE or SOCKET associated with a Prolog stream. As these functions are undocumented anyway, this should not have much impact :slight_smile: They are used internally by the clib packages to implement library(socket) and library(process).

So, by default these headers are no longer included.