Int vs foreign_t in foreign interfaces

Are the values returned from a foreign function, and also the values returned from the various int PL_...() functions always 0 or 1 (FALSE or TRUE)?
(A few functions return something different, such as PL_new_atom(), but they’re defined as returning something different from int, such as atom_t or term_t.)

Reason for the question …

In the foreign interface example, we have the following:

foreign_t
pl_lowercase(term_t u, term_t l)
{    ...
  int rval;
     ...
  rval = PL_unify_atom_chars(l, copy);
     ...
  return rval;
}

foreign_t is a typedef for long unsigned int, so we have (on my machine) a signed 32-bit value being put into an unsigned 64-bit value; and implicit conversions between signed and unsigned in C can be error-prone. However, if rval is always 0 or 1 (FALSE or TRUE), it should be harmless.

Even so, it seems that things would be safer if all the boolean (int) return values were the same, e.g. declare foreign_t rval corresponding to a revised definition foreign_t PL_unify_atom_chars(term_t t, const char *chars).

PS: Some of the PL_put_…() functions, such as PL_put_variable() are documented as not returning anything (return type void) but SWI-Prolog.h has them as return type int. Presumably, this is a documentation typo.

Almost all the API functions are indeed defined as int and always return TRUE or FALSE (1 or 0). foreign_t is uintptr_t, and comes in two flavors. For any predicate it may return TRUE or FALSE. Non-det predicates may return using PL_retry() or PL_retry_address() which pack a small integer or word-aligned address that can be used by the foreign predicate to keep track of the state for backtracking.

There is AFAIK nothing really wrong. I do not see much reason to change the return type of the API functions to foreign_t. It probably mostly results in a lot of compiler warnings for code doing int rc = PL_....().

That is indeed a problem with the docs. Please correct if you find one (just fixed PL_put_variable(), PL_put_bool() and PL_put_atom()). Over the years I’ve moved to a programming style where I put a lot of the API function calls in a large conjunction, so we get code as below. Some of the API functions can technically not fail and used to have a void return type. Void functions are a bit unpractical using this style of programming though. Note that almost all API functions allocate something (often on the Prolog stacks) and can thus result in a resource exception or have other error conditions.

if ( PL_...() && 
     PL_..() &&
     ... )

This is a very useful style, I have seen it in JavaScript as well. How do you deal with the side effects though? (Like the allocations you mention yourself)

All (almost) the PL_*() functions create side effects that are subject to backtracking and/or garbage collection :slight_smile: Typically there is no need to deal with side effects except for non-Prolog side effects created by the foreign predicate.

1 Like