# Cpp2 exceptions

**URL:** <https://swi-prolog.discourse.group/t/cpp2-exceptions/6040>\
**Category:** General\
**Created:** [December 20, 2022, 1:06pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040 "2022-12-20T13:06:06Z")\
**Posts on this page:** 20\
**Page:** 4

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [February 24, 2023, 1:19pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/61 "2023-02-24T13:19:01Z")

</div>

> [@peter.ludemann](#):
>
> I don’t think it’s quite as simple, because my design has two kinds of C++ exceptions: `PlFail()` and `PlException()`

But, when defining a foreign predicate it doesn’t matter too much, does it? In both case the PREDICATE wrapper must return FALSE.

> [@peter.ludemann](#):
>
> What are the circumstances where there could be a cleanup handler?

```prolog
test(X) :-
    setup_call_cleanup(
        true,
        between(1, 5, X),
        throw(error)).

```

Now call this from C(++). The first PL\_next\_solution() says TRUE, but the cleanup is not executed. Now close the query. That runs the cleanup handler and should raise `error`. If the goal in the setup\_call\_cleanup/3 completed (fail, exception, deterministic true), the cleanup handler has done its work before control gets back to Prolog and thus PL\_next\_solution() already generates the exception.

> [@peter.ludemann](#):
>
> Is this for the situation where there is an enclosing `PlEngine`, and no `PREDICATE`?

Yes. To do it all by the book though, we must either use no flag (exception will never reach C(++)) or use PL\_Q\_CATCH\_EXCEPTION as there is nothing to pass to. If we do so however, we must deal with the exception before we close the query (after which we could throw some other exception).

If we use PL\_Q\_PASS\_EXCEPTION here, the exception will be considered “not caught” as there is no enclosing PlQuery and (thus) the system will trap the debugger rather than passing the exception to C.

If that poses problems we can probably provide the same semantics as `PL_Q_PASS_EXCEPTION` but without trapping the debugger. Possibly we could supply both flags for that, meaning it is caught, but it should survive closing the query.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [February 25, 2023, 2:51am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/66 "2023-02-25T02:51:16Z")

</div>

> [@jan](#):
>
> So, I think it is as simple as
> 
> - test that PL\_next\_solution() returned FALSE
> - close the query
> - if an exception is pending, throw it, just like you do with all the other API functions.

I think that will work. It’ll take me a bit of time to make all the code changes and test cases, but the overall API probably won’t change much, if at all. (I’ll also update `swipl-win`, `rocksdb`, and `rolog`)

As for `PlEngine` – I think we can deal with that separately, possibly by making some macros that are similar to what `PREDICATE` does. Or – because C++ now has lambdas – possibly we could do this without macros; I already have one use of lambda, in `bool PlRewindOnFail(std::function<bool()> f)`. The current support for `PlEngine` is fairly minimal, anyway (as was `PlQuery`, so making a good API for `PlEngine` could require quite a bit of thinking).

ADDED:

> [@jan](#):
>
> Using PL\_clear\_exception() is almost always wrong.

Here’s what the code inside `PREDICATE` looks like (some parts removed or changed slightly for brevity/clarity):

```cpp
  try {
  ...
  } catch (const std::bad_alloc&) {
    return PL_resource_error("memory");
  } catch (PlFail&) {
    return false;
  } catch (PlException& ex) {
    return PL_raise_exception(ex.term().C_);
  }

```

A `PlException` can be thrown in two ways:

1. C++ code: `throw PlDomainError(...)` or similar
2. `PlCheck(...)`, which does:

```cpp
      term_t ex = PL_exception(0); 
      if (ex) 
      { // PL_clear_exception(); /* See below */
        throw PlException(ex);
      }

```

In the latter situation, the frame’s exception has already been set, so the `PL_raise_exception()` in `PREDICATE` will act as a no-op. On the other hand, if the `PlCheck()` code does `PL_clear_exception()`, the “catch” in `PREDICATE` would just reinstate the same exception term. So, the net effect is the same, either way.

However, if the code surrounding the `PlCheck()` has its own try/catch and deals with the exception, it would also need to do `PL_clear_exception()` or else there will be a “did not clear exception” message (I presume that the message is caused by `PL_raise_execption(...); return TRUE`).  
So, I think that `PlCheck(...)` should call `PL_clear_exception()` before `throw PlException(ex)`.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [February 28, 2023, 9:24am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/67 "2023-02-28T09:24:07Z")

</div>

> [@peter.ludemann](#):
>
> So, I think that `PlCheck(...)` should call `PL_clear_exception()` before `throw PlException(ex)`.

Seems to make sense. My claim _“using PL\_clear\_exception() is almost always wrong”_ probably only applies to languages that have no exception handling such as C 🙂

Still, the Prolog exceptions should in almost all cases be propagated to Prolog …

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [March 20, 2023, 4:14am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/68 "2023-03-20T04:14:36Z")

</div>

It turns out that C++ exceptions and the Prolog debugger don’t like each other. When a predicate that’s called via `PlQuery` (which uses `PL_open_query()` / `PL_next_solution()`) creates a Prolog error, the debugger gets involved _before_ the C++ code sees it. It appears that the debugger resumes execution using a `longjmp()` - and this bypasses the `catch` in PREDICATE, which results in a fatal runtime error because the exception isn’t handled. (Or it gets a segmentation error – I’ve seen both behaviours.)

This problem can be avoided by running the query inside `catch(Goal,E,true)`, which avoids the debugger getting involved (the debugger looks up the stack for a “catch” before the top level; if it sees a “catch”, it just continues execution without a `longjmp`)

Possibly a `PL_FA_CPP` flag in the PREDICATE could be used to prevent invoking the debugger? – that’s not a proper solution, but at least it would prevent crashes.

The conventional advice for C++ and `longjmp` is “don’t do that”:

> **[Issues with mixing setjmp()/longjmp() with C++ exceptions](https://www.ibm.com/support/pages/issues-mixing-setjmplongjmp-c-exceptions)**
>
> C++ exception handling does not behave as intended when mixing it with setjmp()/longjmp() functions.

The other issue that I’ve been dealing with is the lifetime of Prolog terms and the lifetime of C++ objects. This turns out to be quite tricky:

- throwing a `PlException` exception invokes the `PlException` copy constructor (and the destructor on the original object). This would probably be easier in Rust. 🙂
- if the `PlQuery` predicate creates a Prolog exception (via `PlQuery::next_solution()`), the error term’s lifetime is limited to the foreign frame in which the predicate runs, so the error term is invalid at the point where the C++ code sees it (which is, I suppose, why `PL_open_query()` has the `PL_Q_PASS_EXCEPTION` and `PL_Q_CATCH_EXCEPTION` flags).

I’ve solved these problems for now by having `PlException` use `PL_record_external()` to serialize the error term. This is more overhead than I’d like, but it’s a fairly simple solution; a lower overhead solution looks as if it would be quite tricky to get right. (I tried `PL_record()` but got various crashes, presumably because I was trying to use terms that had become invalid.)

* * *

* * *

Status: all my tests are passing, although I need to add more test cases. I also need to do a bit of clean-up and update the documentation. (There’s one outstanding bug – a memory leak: [PL\_PRUNED not called for C++ foreign predicate · Issue #1150 · SWI-Prolog/swipl-devel · GitHub](https://github.com/SWI-Prolog/swipl-devel/issues/1150))

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 21, 2023, 1:42pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/69 "2023-03-21T13:42:36Z")

</div>

> [@peter.ludemann](#):
>
> When a predicate that’s called via `PlQuery` (which uses `PL_open_query()` / `PL_next_solution()`) creates a Prolog error, the debugger gets involved _before_ the C++ code sees it.

Only if you set PL\_Q\_PASS\_EXCEPTION. You are telling Prolog that you will pass on exceptions to the outer Prolog environment. If that doesn’t catch the exception it is considered _uncaught_ and the debugger gets involved. What happens depends on the user interaction with the debugger. In any case, SWI-Prolog no longer uses setjmp()/longjmp() unless the old PL\_throw\_exception() is used (rather than PL\_raise\_exception()). It also uses setjmp()/longjmp() to deal with GMP allocation failures, but that should be local and never interleaved with C++.

So, if you want to catch the exception you must tell the system so using PL\_Q\_CATCH\_EXCEPTION. That means you must deal with it before closing teh query though. As suggested earlier we could define a flag that will preserve the exception and claims it is caught.

> [@peter.ludemann](#):
>
> throwing a `PlException` exception invokes the `PlException` copy constructor (and the destructor on the original object).

That should be ok, no? That only affects the term\_t reference, not the term itself.

> [@peter.ludemann](#):
>
> I’ve solved these problems for now by having `PlException` use `PL_record_external()`

PL\_record\_external() should never be needed for preserving terms in the same process. It is there to copy terms to other instances, i.e…, it notably serialized atoms rather then using the atom handle. If one works and the other doesn’t you probably have a memory corruption. I still think there is something fundamentally wrong if these functions are needed.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [April 4, 2023, 2:09am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/70 "2023-04-04T02:09:59Z")

</div>

The new `SWI-cpp2.h` is almost ready – all that remains is some tidying up and documentation.  
`packages/swipl-win` and `rocksdb` are compiling and passing their tests (for `swipl-win`, my “test” is to run the executable and run a few simple Prolog queries in the window).

I’ve made a few name changes (e.g., `PlCheck()` is now `PlCheckFail()`); I’ve also created wrappers for all the `PL_*()` functions in `SWI-Prolog.h`, so that there’s no need to check for Prolog errors after a call; they’ll throw a C++ error if something goes wrong.

@mgondan – what is the status of `rolog`? It appears that you’ve been keeping the `cpp2` source in parallel with your other changes - if so, I can make the necessary changes. (I don’t know how to test `rolog,` however).

* * *

> [@jan](#):
>
> [SWI-Prolog](https://www.swi-prolog.org/pldoc/doc_for?object=manual) no longer uses setjmp()/longjmp()

Hmmm … things are now working. “I didn’t change anything”.

> [@jan](#):
>
> PL\_record\_external() should never be needed for preserving terms in the same process

It’s possible that my lifetime problem will go away if I only call `PL_exception()` when `PL_Q_CATCH_EXCEPTION` is set (I also use that flag to decide whether to do `PL_exception(qid)` or `PL_exception(0)`).  
Eventually, I may be able to get rid of the serialization; but for now, it removes one potential source of error. Getting the `PlException` object correct has been tricky - it’s similar to getting a smart pointer correct, and there’s no C++ “borrow checker”.

@jan For testing, I’m provoking an exception by calling a non-existent predicate. Is this sufficient, or should I also provoke some other errors, such as divide-by-zero or must\_be/2?

PS: the top level of `swipl-win` does the wrong thing with `catch(foo(bar),E,true)` – it doesn’t call catch/3 but just outputs the error message – but it does the right thing if I define `f(E):-catch(foo(bar),E,true)` and then call `f(E)` at the top level.

---

<div class="post-metadata">

**Author:** ![mgondan1](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/mgondan1/32/803_2.png) [@mgondan1](https://swi-prolog.discourse.group/u/mgondan1)\
**Post date:** [April 4, 2023, 5:10am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/71 "2023-04-04T05:10:31Z")

</div>

You can test rolog like this:

```prolog
bash$ cd rolog
$ cd ..
$ R CMD check rolog

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [April 4, 2023, 8:56pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/72 "2023-04-04T20:56:09Z")

</div>

I renamed `rolog_cpp2.txt` to `rolog_cpp2.cpp` and it compiled cleanly with my latest `SWI-cpp2.h`. But I seem to be missing some things for testing …

```shell
$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

$ git pull origin --rebase=no
Already up to date.

$ git remote -v
myfork	git@github.com:kamahen/rolog.git (fetch)
myfork	git@github.com:kamahen/rolog.git (push)
origin	git@github.com:mgondan/rolog.git (fetch)
origin	git@github.com:mgondan/rolog.git (push)

$ git log -n 1
commit 4ab15be6891a1e4b640fcff962448e65e1c08642 (HEAD -> main, origin/main, origin/HEAD)
Author: mgondan <Matthias.Gondan-Rochon@uibk.ac.at>
Date: Sun Jan 29 23:30:49 2023 +0100

    Update interval.pl

$ (cd ..; R CMD check rolog)
* using log directory ‘/home/peter/src/rolog.Rcheck’
* using R version 4.0.4 (2021-02-15)
* using platform: x86_64-pc-linux-gnu (64-bit)
* using session charset: UTF-8
* checking for file ‘rolog/DESCRIPTION’ ... ERROR
Required field missing or empty:
  ‘Author’
* DONE

Status: 1 ERROR
See
  ‘/home/peter/src/rolog.Rcheck/00check.log’
for details.

```

(`00check.log` has the same contents as the error messages)

I then edited `DESCRIPTION`:

```diff
$ git diff -U1
diff --git a/DESCRIPTION b/DESCRIPTION
index 6364083..aaa501b 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -5,3 +5,3 @@ Version: 0.9.12
 Date: 2023-01-27
-Authors@R: c(person("Matthias", "Gondan", role=c("aut", "com", "cre"),
+Author: c(person("Matthias", "Gondan", role=c("aut", "com", "cre"),
   email="Matthias.Gondan-Rochon@uibk.ac.at", comment="Universität Innsbruck"),

```

and got a different error (needs package `rswipl`, plus some suggested packages):

```shell
$ (cd .. && R CMD check rolog)
* using log directory ‘/home/peter/src/rolog.Rcheck’
* using R version 4.0.4 (2021-02-15)
* using platform: x86_64-pc-linux-gnu (64-bit)
* using session charset: UTF-8
* checking for file ‘rolog/DESCRIPTION’ ... OK
* checking extension type ... Package
* this is package ‘rolog’ version ‘0.9.12’
* package encoding: UTF-8
* checking package namespace information ... OK
* checking package dependencies ... ERROR
Package required but not available: ‘rswipl’

Packages suggested but not available:
  'DiagrammeR', 'DiagrammeRsvg', 'rsvg', 'testthat'

The suggested packages are required for a complete check.
Checking can be attempted without them by setting the environment
variable _R_CHECK_FORCE_SUGGESTS_ to a false value.

See section ‘The DESCRIPTION file’ in the ‘Writing R Extensions’
manual.
* DONE

Status: 1 ERROR
See
  ‘/home/peter/src/rolog.Rcheck/00check.log’
for details.

```

---

<div class="post-metadata">

**Author:** ![mgondan1](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/mgondan1/32/803_2.png) [@mgondan1](https://swi-prolog.discourse.group/u/mgondan1)\
**Post date:** [April 5, 2023, 9:28am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/73 "2023-04-05T09:28:15Z")

</div>

I have moved all the swipl sources from rolog to rswipl. If you just want to “install” rolog, you can connect to swipl on your system. If you want to test it, you need the above-mentioned R packages, therefore,

```prolog
$ R
> install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", "rsvg", "testthat"))
> quit()
$

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [April 5, 2023, 5:46pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/74 "2023-04-05T17:46:21Z")

</div>

One thing I noticed while updating the `rolog_cpp2.txt` is that in a number of places you have

```cpp
if(pl.is_atom() && pl == "na")

```

which, because of new deprecations, becomes:

```cpp
if(pl.is_atom() && pl.as_string() == "na")

```

(The generated code is the essentially same; but I’ve deprecated the `==` operator because it doesn’t allow specifying the encoding)

This would be more efficient as:

```cpp
static PlAtom ATOM_na("na");
if(pl.is_atom() && pl.as_atom() == ATOM_na)

```

and I’ve added a new method to make this easier:

```cpp
static PlAtom ATOM_na("na");
if(pl.eq_if_atom(ATOM_na)

```

Anyway, I’m still unable to test … it seems that Debian is a bit conservative in its use of packages and I need to learn some magic incantations for adding PPAs. ☹  
Also, there seem to be some additional package dependencies:

```shell
 r-base-core : Depends: libicu66 (>= 66.1-1~) but it is not installable
               Depends: libjpeg8 (>= 8c) but it is not installable

```

I have an Ubuntu machine with R 4.2.3 on it, but it also has some problems.

Maybe I’ll just have to leave this to @mgondan to test.

Here’s what I get on Debian (ChromeBook):

> [@mgondan1](#):
>
> ```prolog
> > install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", "rsvg", "testthat"))
> 
> ```

```bash
> warnings()
Warning messages:
1: package ‘rswipl’ is not available for this version of R
‘rswipl’ version 9.1.8 is in the repositories but depends on R (>= 4.2)

A version of this package for your version of R might be available elsewhere,
see the ideas at
https://cran.r-project.org/doc/manuals/r-patched/R-admin.html#Installing-packages
2: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘isoband’ had non-zero exit status
3: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘farver’ had non-zero exit status
4: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘rsvg’ had non-zero exit status
5: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘igraph’ had non-zero exit status
6: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘influenceR’ had non-zero exit status
7: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘scales’ had non-zero exit status
8: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘vroom’ had non-zero exit status
9: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘ggplot2’ had non-zero exit status
10: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘dplyr’ had non-zero exit status
11: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘readr’ had non-zero exit status
12: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘tidyr’ had non-zero exit status
13: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘viridis’ had non-zero exit status
14: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘DiagrammeR’ had non-zero exit status
15: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘testthat’ had non-zero exit status

```

```prolog
$ R CMD check rolog
Warning: ‘rolog’ is neither a file nor directory, skipping

[peter@penguin rolog (exceptions)]$ cd ..
[peter@penguin src]$ R CMD check rolog
* using log directory ‘/home/peter/src/rolog.Rcheck’
* using R version 4.0.4 (2021-02-15)
* using platform: x86_64-pc-linux-gnu (64-bit)
* using session charset: UTF-8
* checking for file ‘rolog/DESCRIPTION’ ... OK
* checking extension type ... Package
* this is package ‘rolog’ version ‘0.9.12’
* package encoding: UTF-8
* checking package namespace information ... OK
* checking package dependencies ... ERROR
Package required but not available: ‘rswipl’

Packages suggested but not available: 'DiagrammeR', 'rsvg', 'testthat'

The suggested packages are required for a complete check.
Checking can be attempted without them by setting the environment
variable _R_CHECK_FORCE_SUGGESTS_ to a false value.

See section ‘The DESCRIPTION file’ in the ‘Writing R Extensions’
manual.
* DONE

```

And on my Ubuntu 20.04 machine:

```shell
Warning messages:
1: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘curl’ had non-zero exit status
2: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘rsvg’ had non-zero exit status
3: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘igraph’ had non-zero exit status
4: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘V8’ had non-zero exit status
5: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘influenceR’ had non-zero exit status
6: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘DiagrammeRsvg’ had non-zero exit status
7: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘vroom’ had non-zero exit status
8: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘readr’ had non-zero exit status
9: In install.packages(c("rswipl", "DiagrammeR", "DiagrammeRsvg", ... :
  installation of package ‘DiagrammeR’ had non-zero exit status

```

---

<div class="post-metadata">

**Author:** ![mgondan1](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/mgondan1/32/803_2.png) [@mgondan1](https://swi-prolog.discourse.group/u/mgondan1)\
**Post date:** [April 5, 2023, 6:10pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/75 "2023-04-05T18:10:01Z")

</div>

It’s a bit strange that your Linux distributions don’t allow you to install a current R. Anyway, I wouldn’t mind a pull request with your much appreciated results (even an incomplete one).

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [April 5, 2023, 6:22pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/76 "2023-04-05T18:22:53Z")

</div>

> [@mgondan1](#):
>
> It’s a bit strange that your Linux distributions don’t allow you to install a current R.

There seem to be some extra steps needed with key signing for adding a PPA to Debian, and I haven’t spent the time figuring out how to do it. (This also affects the PPA for SWI-Prolog; but I just build it form source.)

As soon as I have the new `SWI-cpp2.h` ready for review, I’ll inform you. (There have been a _lot_ of changes, although I think that most of them are backwards compatible.)

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [April 8, 2023, 9:20pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/77 "2023-04-08T21:20:10Z")

</div>

PR is ready for review: [ENHANCED: C++-compatible exception handling + more wrapper functions and classes by kamahen · Pull Request #39 · SWI-Prolog/packages-cpp · GitHub](https://github.com/SWI-Prolog/packages-cpp/pull/39) is ready for review.

Here are the main changes:

- `SWI-cpp2-plx.h` contains wrapper functions (imported from `SWI-cpp2.h`)
- throw `PlException` for Prolog errors
- most `SWI-Prolog.h` functions have a wrapper.
- `verify()` methods removed from `PlAtom`, `PlTerm`, etc.
- the wrapper functions do the checking.
- Some executable code has been moved to `SWI-cpp2.cpp`
- can be inlined, if desired.
- `PlException` is now a subclass of `std::exception`, not a subclass of `PlTerm`.
- `PlTypeError`, `PlDomainError`, etc. are no longer subclasses of `PlTerm`, but are functions for creating suitable PlException objects.
- The string comparison operators are deprecated; use `as_string()` and `std::string` comparison instead, which allows specifying the encoding.
- Added `PlRecord`, `PlRecordExternal`, `PlControl` (used by `PREDICATE_NONDET`), `PlStream`.
- Fixed numerous bugs and misfeatures; added tests.

I’ve also made PRs for swipl-win, rocksdb, and rolog.

I apologize for the large size of the PR, but I didn’t see an easy way to break it into smaller pieces.

The documentation is still a bit rough; I’ll try to review it and improve it “in the future”.

There are still a few things that need to be done, mostly to do with text encodings. However, their lack shouldn’t be a significant issue, and missing functionality should be easy to add.

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [April 29, 2023, 2:58pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/78 "2023-04-29T14:58:02Z")

</div>

I’m mostly finished with changes to `SWI-cpp2.h`. My changes have been merged and will probably be available in the next development release (9.1.9).

The main things left to do are:

- Improve the interfaces for blobs and PL\_options()
- Add encoding information for methods that accept or return strings (`char*`, `wchar_t*`, `std::string`, `std::string`) - this is partially done but needs to be finished.
- Improve the efficiency of `PlException` - currently it uses the PL\_record() et al to ensure that the exception term doesn’t go out of scope but some changes to `PlQuery` might make this unnecessary. (Also, more test cases are needed)
- Improve the documentation. PRs gratefully accepted. 🙂

I intend to use the improved C++ API with the `rocksdb` package and the related `rocks-predicates` package, for removing size limitations when scaling Prolog databases (`redis` is another possibility).

---

<div class="post-metadata">

**Author:** ![mgondan1](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/mgondan1/32/803_2.png) [@mgondan1](https://swi-prolog.discourse.group/u/mgondan1)\
**Post date:** [December 22, 2025, 8:55pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/79 "2025-12-22T20:55:16Z")

</div>

I am sorry to report ASAN/UBSAN problems with exceptions :-/

To reproduce, use a current clang-21 and then

```prolog
CC="clang-21 -fsanitize=address,undefined -fno-sanitize=float-divide-by-zero -fno-sanitize=alignment -fno-sanitize=function -fno-omit-frame-pointer -std=gnu23" CXX="clang++-21 -fsanitize=address,undefined -fno-sanitize=float-divide-by-zero -fno-sanitize=alignment -fno-omit-frame-pointer -frtti -std=gnu++17" CFLAGS="-g -O3 -Wall " LDFLAGS="-Wl,-O1" CXXFLAGS="-g -O3 -Wall -pedantic -fpic" cmake -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=~/swipl -DMACOSX_DEPENDENCIES_FROM=Homebrew -DSWIPL_INSTALL_IN_LIB=ON -DINSTALL_DOCUMENTATION=OFF -DBUILD_TESTING=ON -DINSTALL_TESTS=ON -DSWIPL_PACKAGES_JAVA=OFF -DSWIPL_PACKAGES_GUI=OFF -DSWIPL_PACKAGES_ODBC=OFF -DSWIPL_PACKAGES_PYTHON=OFF -S ..
make
ctest

```

The cpp-test complains, that `% [4/248] cpp:hello ..AddressSanitizer: CHECK failed: asan_interceptors.cpp:463 "(( __interception::real___ cxa_throw)) != (0)" (0x0, 0x0) (tid=536295)`

Stack trace:

```prolog
% [4/256] cpp:hello ..AddressSanitizer: CHECK failed: asan_interceptors.cpp:463 "(( __interception::real___ cxa_throw)) != (0)" (0x0, 0x0) (tid=251293)
    #0 0x6363259e8b25 in __asan::CheckUnwind() asan_rtl.cpp.o
    #1 0x636325a04096 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/home/matthias/swipl-devel/build.make.clang.ubsan/src/swipl+0xf7096) (BuildId: fc24f3c680c5e134d398868d5877bc54cead8a7a)
    #2 0x6363259c2934 in __cxa_throw (/home/matthias/swipl-devel/build.make.clang.ubsan/src/swipl+0xb5934) (BuildId: fc24f3c680c5e134d398868d5877bc54cead8a7a)
    #3 0x76cda9180514 in PlEx_fail(__PL_queryRef*) /home/matthias/swipl-devel/packages/cpp/SWI-cpp2.cpp:100:5
    #4 0x76cda91801b0 in PlTerm::get_nchars[abi:cxx11](unsigned int) const /home/matthias/swipl-devel/packages/cpp/SWI-cpp2.cpp:120:3
    #5 0x76cda914f0c2 in PlTerm::as_string[abi:cxx11](PlEncoding) const /home/matthias/swipl-devel/packages/cpp/SWI-cpp2.h:634:12
    #6 0x76cda914f0c2 in pl_hello__2(PlTermv) /home/matthias/swipl-devel/packages/cpp/test_cpp.cpp:110:28

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [December 26, 2025, 7:12am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/80 "2025-12-26T07:12:51Z")

</div>

I will try to reproduce this next week. Does it require clang or can gcc be used?

---

<div class="post-metadata">

**Author:** ![mgondan1](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/mgondan1/32/803_2.png) [@mgondan1](https://swi-prolog.discourse.group/u/mgondan1)\
**Post date:** [December 26, 2025, 10:54am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/81 "2025-12-26T10:54:37Z")

</div>

You also get an error message with gcc (though less detailed).

```prolog
C="gcc -fsanitize=address,undefined -fno-sanitize=float-divide-by-zero -fno-sanitize=alignment -fno-omit-frame-pointer -std=gnu23" CXX="g++ -fsanitize=address,undefined -fno-sanitize=float-divide-by-zero -fno-sanitize=alignment -fno-omit-frame-pointer -frtti -std=gnu++17" CFLAGS="-g -O3 -Wall " LDFLAGS="-Wl,-O1" CXXFLAGS="-g -O3 -Wall -pedantic -fpic" cmake -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=~/swipl -DMACOSX_DEPENDENCIES_FROM=Homebrew -DSWIPL_INSTALL_IN_LIB=ON -DINSTALL_DOCUMENTATION=OFF -DBUILD_TESTING=ON -DINSTALL_TESTS=ON -DSWIPL_PACKAGES_JAVA=OFF -DSWIPL_PACKAGES_GUI=OFF -DSWIPL_PACKAGES_ODBC=OFF -DSWIPL_PACKAGES_PYTHON=OFF -S .. && make && ctest

```

My output (ubuntu):

```prolog
matthias@hp:~/swipl-devel/build.make.gcc.ubsan$ ctest --rerun-failed --output-on-failure
Test project /home/matthias/swipl-devel/build.make.gcc.ubsan
    Start 49: cpp:cpp
1/1 Test #49: cpp:cpp ..........................Subprocess aborted***Exception: 0.50 sec
% Start unit: cpp
% [1/256] cpp:unwrap .......................................................................................................................... passed (0.028 sec)
% [2/256] cpp:hello ........................................................................................................................... passed (0.000 sec)
% [3/256] cpp:hello ........................................................................................................................... passed (0.000 sec)
% [4/256] cpp:hello ..terminate called after throwing an instance of 'PlTerm'

0% tests passed, 1 tests failed out of 1

Total Test time (real) = 0.51 sec

The following tests FAILED:
         49 - cpp:cpp (Subprocess aborted)
Errors while running CTest

```

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [December 26, 2025, 2:46pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/82 "2025-12-26T14:46:55Z")

</div>

Please open a bug for this and assign it to me.  
The problem appears to be that PL\_get\_nchars() failed; but it’s not clear to me why it fails with ASAN/UBSAN and not otherwise.

---

<div class="post-metadata">

**Author:** ![mgondan1](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/mgondan1/32/803_2.png) [@mgondan1](https://swi-prolog.discourse.group/u/mgondan1)\
**Post date:** [December 26, 2025, 4:21pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/83 "2025-12-26T16:21:02Z")

</div>

See here,

> <https://github.com/SWI-Prolog/packages-cpp/issues/102>
>
> to reproduce
> 
> \`\`\`
> cd swipl-devel
> (change Ninja to "Unix\\\\ Makefiles" in scripts/…configure script
> mkdir build.clang.asan
> cd build.clang.asan
> ../scripts/configure
> make
> ctest
> \`\`\`
> 
> The hello test with hello("世界", \_Out) fails. I think it is the exception itself that fails:
> 
> \`\`\`
> % \[3/256\] cpp:hello ............................................................................................................. passed (0.001 sec)
> % \[4/256\] cpp:hello ..AddressSanitizer: CHECK failed: asan\_interceptors.cpp:463 "((\_\_interception::real\_\_\_cxa\_throw)) != (0)" (0x0, 0x0) (tid=100242)
> #0 0x63ccdbb1efa5 in \_\_asan::CheckUnwind() asan\_rtl.cpp.o
> #1 0x63ccdbb39fe6 in \_\_sanitizer::CheckFailed(char const\*, int, char const\*, unsigned long long, unsigned long long) (/home/matthias/swipl-devel/build.clang.asan/src/swipl+0xf6fe6) (BuildId: 922187003c1a2d84768253af0e47d9750e11344f)
> #2 0x63ccdbaf7e64 in \_\_cxa\_throw (/home/matthias/swipl-devel/build.clang.asan/src/swipl+0xb4e64) (BuildId: 922187003c1a2d84768253af0e47d9750e11344f)
> #3 0x7f2ab8bb948f in PlEx\_fail(\_\_PL\_queryRef\*) /home/matthias/swipl-devel/packages/cpp/SWI-cpp2.cpp:100:5
> #4 0x7f2ab8bb8e85 in PlTerm::get\_nchars\[abi:cxx11\](unsigned int) const /home/matthias/swipl-devel/packages/cpp/SWI-cpp2.cpp:120:3
> #5 0x7f2ab8b8aa08 in PlTerm::as\_string\[abi:cxx11\](PlEncoding) const /home/matthias/swipl-devel/packages/cpp/SWI-cpp2.h:634:12
> #6 0x7f2ab8b8aa08 in pl\_hello\_\_2(PlTermv) /home/matthias/swipl-devel/packages/cpp/test\_cpp.cpp:110:28
> #7 0x7f2ab8b8aa08 in \_pl\_hello\_\_2(unsigned long, int, \_\_PL\_foreign\_context\*) /home/matthias/swipl-devel/packages/cpp/test\_cpp.cpp:105:1
> \`\`\`

I cannot “assign” it to you, probably @jan has to do this. Thank you for considering it.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [December 27, 2025, 12:36pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/84 "2025-12-27T12:36:47Z")

</div>

> [@mgondan1](#):
>
> I cannot “assign” it to you, probably @jan has to do this. Thank you for considering it.

Somehow that seems not possible either. @peter.ludemann should be able to assign himself.

[Previous page](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040.md?page=3)

[Next page](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040.md?page=5)
