# 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:** 1

<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 20, 2022, 1:06pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/1 "2022-12-20T13:06:06Z")

</div>

This is the old c++ interface:

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ cat likes.cpp
#include "SWI-cpp.h"
#include <iostream>

int main(int argc, char** argv)
{
    PlEngine e(argc, argv) ;
    PlTermv noargs(0) ;
    PlQuery q("license1", noargs) ;

    try
    { q.next_solution() ;
    }

    catch(PlException& ex)
    { std::cerr << (char*) ex << std::endl ;
    }
    return 0 ;
}

```

Running it:

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ swipl-ld likes.cpp
matthias@DESKTOP-A2T8IFC:~/exception$ ./a.out
Welcome to SWI-Prolog (threaded, 64 bits, version 9.1.2-4-g65fab31ed)
(etc.)
For built-in help, use ?- help(Topic). or ?- apropos(Word).
error(existence_error(procedure,license1/0),context(system:'$c_call_prolog'/0,_6776))

```

This is the new one:

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ cat likes2.cpp
#include "SWI-cpp2.h"
#include <iostream>

int main(int argc, char** argv)
{
    PlEngine e(argc, argv) ;
    PlTermv noargs(0) ;
    PlQuery q("license1", noargs) ;

    try
    { if(!q.next_solution())
        std::cerr << "query failed" << std::endl ;
    }

    catch(PlException& ex)
    { std::cerr << "caught exception" << std::endl ;
      std::cerr << ex.as_string() << std::endl ;
    }
    return 0 ;
}

```

Compilation and run:

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ swipl-ld likes2.cpp
matthias@DESKTOP-A2T8IFC:~/exception$ ./a.out
Welcome to SWI-Prolog (threaded, 64 bits, version 9.1.2-4-g65fab31ed)
(etc.)
For built-in help, use ?- help(Topic). or ?- apropos(Word).

query failed
matthias@DESKTOP-A2T8IFC:~/exception$

```

What do I need to do to catch the exception? I played around with the flags (optional 3rd argument to PlQuery), it didn’t really help.

---

<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 20, 2022, 1:19pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/2 "2022-12-20T13:19:43Z")

</div>

> [@mgondan1](#):
>
> What do I need to do to catch the exception?

I _think_ calling PlCheck(PL\_next\_solution()). I also think this is wrong and the old should still work. Let us wait for @peter.ludemann 🙂

---

<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 20, 2022, 1:38pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/3 "2022-12-20T13:38:23Z")

</div>

This one just terminates the program, and I think PlCheck is void, so that I cannot obtain the return value telling me if the query succeeds or fails.

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ cat likes2.cpp
#include "SWI-cpp2.h"
#include <iostream>

int main(int argc, char** argv)
{
    PlEngine e(argc, argv) ;
    PlTermv noargs(0) ;
    PlQuery q("license1", noargs) ;

    try
    { PlCheck(q.next_solution()) ;
    }

    catch(PlException& ex)
    { std::cerr << "caught exception" << std::endl ;
      std::cerr << ex.as_string() << std::endl ;
    }
    return 0 ;
}

```

Output

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ ./a.out
(welcome etc.)

terminate called after throwing an instance of 'PlFail'
Aborted

```

Let’s see if @peter.ludemann can help.

---

<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 20, 2022, 1:49pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/4 "2022-12-20T13:49:18Z")

</div>

I am at the airport, so cannot help very much at the moment …  
I tried to preserve compatibility with the old interface but it’s possible that I got this wrong. There should be some examples that could help you in the test cases.

---

<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 20, 2022, 2:06pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/5 "2022-12-20T14:06:09Z")

</div>

It seems the current interface upsets many of the “embedding” scenarios. When writing a foreign predicate in C++, all magic is done using the PREDICATE() wrapper to eventually get the proper exception. I.e., a failure from an API is mapped to `throw PlFail`, which the PREDICATE() wrapper translates into `return false`, which causes the Prolog runtime system to find the raised Prolog exceptions and perform a Prolog throw/1.

This used to be different, where the C++ API wrappers checked the `false` return code and when associated with an exception they packaged the Prolog exception as a C++ exception. As a result, a Prolog API could return false for logical failure or throw a C++ exception that represents the Prolog exception.

This requires some thought. As is, most failures throw PlFail. This should be caught, after which we must inspect the Prolog environment to verify whether there is an exception or logical failure. It probably requires some creativity to make this look natural again …

---

<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 20, 2022, 2:29pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/6 "2022-12-20T14:29:14Z")

</div>

PlFail is new and separate from PlException. Both are caught in PREDICATE. PlFail shouldn’t be part of any API methods; it should be only a convenience, for avoiding Golang-style cascading error checks. (See also the definition of PlCheck) These should be documented; if not clear, please tell mec what is confusing.

I probably won’t be able to say anything definitive for 24 hours or so

---

<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 20, 2022, 2:32pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/7 "2022-12-20T14:32:57Z")

</div>

This here sort-of works:

```prolog
#include "SWI-cpp2.h"
#include <iostream>

int main(int argc, char** argv)
{
    PlEngine e(argc, argv) ;
    PlTermv noargs(0) ;
    PlQuery q("license1", noargs, PL_Q_PASS_EXCEPTION | PL_Q_EXT_STATUS) ;

    try
    { int r = q.next_solution() ;
      if(r == PL_S_FALSE)
        std::cerr << "failed" << std::endl ;

      if(r == PL_S_TRUE)
        std::cerr << "query successful" << std::endl ;

      if(r = PL_S_EXCEPTION)
      { std::cerr << "returned with an exception" << std::endl ;
        PlException_qid ex ;
        std::cerr << ex.as_string() << std::endl ;
      }
    }

    catch(PlException& ex)
    { std::cerr << "caught exception" << std::endl ;
      std::cerr << ex.as_string() << std::endl ;
    }
    return 0 ;
}

```

It’s not pretty because of the message about current\_prolog\_flag, but I get some information.

```prolog
matthias@DESKTOP-A2T8IFC:~/exception$ ./a.out
(welcome)

returned with an exception
Thread 1 (main): foreign predicate system:current_prolog_flag/2 did not clear exception:
        error(existence_error(procedure,license1/0),context(system: $c_call_prolog/0,_6776))
'$c_call_prolog'/0: Unknown procedure: license1/0
  However, there are definitions for:
        license/0
        license/1
        license/2

```

---

<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 20, 2022, 2:52pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/8 "2022-12-20T14:52:25Z")

</div>

> [@peter.ludemann](#):
>
> PlFail shouldn’t be part of any API methods; it should be only a convenience, for avoiding Golang-style cascading error checks.

That could be the theory. It seems the implementation is quite different though ☹ Just scanning SWI-cpp2.h we see a lot of `throw PlFail`. Most APIs are also wrapped into chkex(), which is defined as

```prolog
inline bool
PlTerm::chkex(int rc)
{ if ( rc )
    return rc;
  throw PlFail();
}

```

So, a failure of the API gets a PlFail exception. For embedding we’d assume something like

```prolog
inline bool
PlTerm::chkex(int rc)
{ term_t ex;
  if ( !rc && (ex=PL_exception(0)) )
    throw PlException(ex);
  return rc;
}

```

Enjoy your trip!

---

<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 20, 2022, 8:51pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/9 "2022-12-20T20:51:45Z")

</div>

I had a few minutes to look into this … and I’m not sure what’s going on.

I ran one of the `hello_call` tests in `test_cpp.pl` manually:

```prolog
?- catch(hello_call(writeln_wrong(hello(世界四))), E, true).
E = error(existence_error(procedure, writeln_wrong/1), context(system:call/1, _)).

```

which seems to be what you expect from your code.

The body of `hello_call/1` is pretty simple (and also wrong – see below):

```cpp
PREDICATE(hello_call, 1)
{ PlCheck(PlCall(A1));
  return true;
}

```

If you look into that, you’ll see that `PlCall()` is:

```cpp
int
PlCall(const std::string& predicate, const PlTermv& args, int flags = PL_Q_PASS_EXCEPTION)
{ PlQuery q(predicate, args, flags);
  return q.next_solution();
}

```

I shouldn’t have used `PlCheck()` with this … the idea behind `PlCheck()` is that if there’s a “false” return code, it throws `PlFail()` – if the “false” was called by an exception, then the exception information has already been recorded, so the top level of PREDICATE will just return `false` and the normal exception handling will be done in Prolog. However, in the case of `PlQuery::next_solution()`, which wraps `PL_next_solution()`, there are results that can indicate failure that aren’t “false” (e.g. `PL_S_EXCEPTION`).

(So, I lied when I said that the API shouldn’t use `PlCheck()` – it does in a few places, but in contexts where the failure is due to an exception)

In this particular situation, it seems that the call to `next_solution()` returns `PL_S_FALSE`, so perhaps there’s a bug in `PL_next_solution()`? (I think it should have returned `PL_S_EXCEPTION`)

Here’s my modified code `mgondan1_likes2.cpp`:

```cpp
#include "SWI-cpp2.h"
#include <iostream>

int main(int argc, char** argv)
{
    PlEngine e(argc, argv) ;
    PlQuery q("license1", PlTermv(), PL_Q_PASS_EXCEPTION) ;

    try
    { int rc = q.next_solution();
      const char *ex_str;
      switch ( rc )
      { case PL_S_EXCEPTION: ex_str = "PL_S_EXCEPTION"; break;
        case PL_S_FALSE: ex_str = "PL_S_FALSE"; break;
        case PL_S_TRUE: ex_str = "PL_S_TRUE"; break;
        case PL_S_LAST: ex_str = "PL_S_LAST"; break;
        case PL_S_YIELD: ex_str = "PL_S_YIELD"; break;
        default: ex_str = "????"; break;
      }
      std::cerr << "query failed: " << rc << ": " << ex_str << std::endl ;
    }

    catch(PlException& ex)
    { std::cerr << "caught exception" << std::endl ;
      std::cerr << ex.as_string() << std::endl ;
    }
    return 0 ;
}

```

```shell
$ swipl-ld mgondan1_likes2.cpp && ./a.out
Welcome to SWI-Prolog (threaded, 64 bits, version 9.1.0-61-g1d24d79ed-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).

query failed: 0: PL_S_FALSE

```

---

<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 21, 2022, 10:32am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/10 "2022-12-21T10:32:25Z")

</div>

As a general design philosophy, constructors can throw exceptions if they can’t properly construct an object (e.g., resource error or type error). This can be either `PlFail` or `PlException`, depending on the situation. The complication is that `PlFail` can have an underlying Prolog exception (this is part of the design philosophy in `SWI-Prolog.h` foreign language interface), and I’ve made `PlFail` and `PlException` separate. Perhaps I should make `PlFail` a subclass of `PlException`? … I need to think about this, and want to also solicit other people’s opinions. (A separate issue is whether we should retain compatibility with existing “version 1” C++ code.)  
I wrote a bit about this in the “Design philosophy of the classes” section of the documentation.

`PlFail` has another use, which is to allow a return-to-Prolog when failure occurs, e.g. `PlCheck(A1.unify_integer(0))` instead of `if (!A1.unify_integer(0)) return false`. Note that this also covers the situation where the method call creates a Prolog exception – the appropriate handling is done by a combination of PREDICATE and the Prolog foreign language interface.

I hope nobody is in a hurry for me to resolve this – it appears that I changed my design approach but didn’t go carefully through `PlQuery` and related interfaces.

I’ll re-read my code and try to do a proper fix for things; but I won’t have much time for this until the end of the year.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [December 21, 2022, 10:45am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/11 "2022-12-21T10:45:23Z")

</div>

> [@peter.ludemann](#):
>
> I hope nobody is in a hurry for me to resolve this

I am not.

Still desire correct code before incorrect faster code or buggy code rushed into usage. 🙂

---

<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 21, 2022, 10:50am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/12 "2022-12-21T10:50:58Z")

</div>

> [@peter.ludemann](#):
>
> I hope nobody is in a hurry for me to resolve this

I’ll use cpp1 in the meantime, no rush.

---

<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 21, 2022, 11:09am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/13 "2022-12-21T11:09:46Z")

</div>

> [@peter.ludemann](#):
>
> In this particular situation, it seems that the call to `next_solution()` returns `PL_S_FALSE`, so perhaps there’s a bug in `PL_next_solution()`? (I think it should have returned `PL_S_EXCEPTION`)

Nope. You need to add `PL_Q_EXT_STATUS` to get the extended status messages. Without it uses the usual TRUE/FALSE return with an exception in PL\_exception(0) if `PL_Q_PASS_EXCEPTION` is passed (otherwise the exception can be retrieved using PL\_exception(qid), but is not passed into the environment). The extended status was implemented when _yield_ was added. As we needed one extra anyway, this was a good moment to also return the other useful status indicators 🙂

Back to the original issue, I don’t think it is too bad. As is, wrapped C APIs now mostly throw PlFail, regardless of whether an exception happened. That is fine, as the PREDICATE wrapper turns this into return with `FALSE` to Prolog and all works as expected. It does not work if we are not defining a Prolog predicate in C++ and (thus) there is no PREDICATE wrapper.

I created a branch “exceptions” with a (partial) solution to this problem. Partial in the sense that not all paths are covered. It makes @mgondan1’s original code work again. And still passes @peter.ludemann’s tests.

I think the design should be that C++ methods and classes should always propagate the original Prolog exception. I’m not sure about PlFail. Possibly it should only be thrown by PlCheck()? Surely (back to partial), there are several places that are not ready for embedding. See e.g., PlTermv

```prolog
  { if ( size_ && a0_ == PlTerm::null )
      throw PlFail();

```

If PL\_new\_term\_refs() fails, we _know_ there is a Prolog exception and should forward that. We could use the (new) PlCheck(0) here. That will forward the current Prolog exception or throw PlFail (which should not happen).

The reasoning behind the C API is that it returns success or failure. On failure it could be logical failure or an error. Logical failure only applies to a part of the API such as the `*_unify_*` functions and PL\_next\_solution(). Then there is the PL\_get\__() group that returns silent failure if the input is incorrect. The C++ version should be using the derived PL\_get\__\_ex() group that fails with an exception. That is another area with issues. For example

```prolog
inline bool PlTerm::operator ==(int64_t v) const
{ int64_t v0;

  if ( PL_get_int64(C_, &v0) )
    return v0 == v;

  throw PlTypeError("integer", *this);
}

```

That should use PL\_get\_int64\_ex(), as there are at least three errors (instantiation, type and int64 range). I think this should be:

```prolog
inline bool PlTerm::operator ==(int64_t v) const
{ int64_t v0;

  PlCheck(PL_get_int64_ex(C_, &v0));
  
  return v0 == v;
}

```

---

<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 21, 2022, 11:43am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/14 "2022-12-21T11:43:08Z")

</div>

> [@jan](#):
>
> I created a branch “exceptions” with a (partial) solution to this problem

I’ll merge this with what I’m working on, plus the bugs that @jan has noted. (Hopefully by year-end)

Amongst other things, I’ve created an `enum` to encapsulate the return codes from `next_solution()`, similar to `PlEncoding`, plus some convenience functions for checking the result. Unfortunately, C++ does automatic conversions between enums and integers, so it’s not obvious to me how to cause a compile-time error if the code does a “bool”-like check on the result of `next_solution()` (which is complicated by the use of `PL_Q_PASS_EXCEPTION` etc). This will require at least another cup of coffee and more thought before I propose a solution … I might be able to produce a safer version by encapsulating `PlQueryRc` in a class and removing the `bool` operator but this will require some reading of the C++ documentations plus some experimentation with the C++ compilers.

> [@jan](#):
>
> The extended status was implemented when _yield_ was added.

That would explain why version 1 of the API works and version 2 doesn’t (with my misunderstanding of the flag).

> [@EricGT](#):
>
> Still desire correct code before incorrect faster code or buggy code rushed into usage. 🙂

I completely agree; in this case, we’re trying to avoid a “buggy _design_”.

---

<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 21, 2022, 12:24pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/15 "2022-12-21T12:24:14Z")

</div>

> [@peter.ludemann](#):
>
> I’ve created an `enum` to encapsulate the return codes from `next_solution()`

Note that in most standard solutions we simply want this.

```prolog
  try
  { while (q.next_solution()) )
      <handle answer>
  } catch ...

```

The extended return codes are not often required. The distinction between an answer and the last answer is probably rarely interesting.

Using `PL_Q_PASS_EXCEPTION` is probably what you want as it makes exception handling consistent with the rest of the API. Note that after an exception we need to return control to Prolog or use PL\_clear\_exception(). If we are not defining a predicate there is no way to return, so we should call PL\_clear\_exception() before continuing.

---

<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 22, 2022, 11:47am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/16 "2022-12-22T11:47:44Z")

</div>

Leaving aside the question of what `next_solution()` should return for the moment, there’s a more fundamental question of how exceptions are handled.

Take, for example, `PlTerm::as_float()`. It returns a `double`, so it must throw an exception if the term isn’t a float. The code is:

```cpp
double PlTerm::as_float() const
{ double v;
  PlCheck(PL_get_float_ex(C_, &v));
  return v;
}

```

where the `PlCheck` does `throw PlFail()` if `PL_get_float_ex()` returns `false`. The `PlFail()` is caught by `PREDICATE` and turned into `return false` to the foreign language interface, which recognizes that an exception has been set (by `PL_get_float_ex()`), and that exception can be caught by catch/3.

On the other hand, consider `PlTerm::arity()` … there is no `PL_get_name_arity_ex()`, so the code uses `PL_get_name_arity()` and throws a `PlException`:

```cpp
size_t PlTerm::arity() const
{ size_t arity;
  if ( !PL_get_name_arity(C_, nullptr, &arity) )
    throw PlTypeError("compound", *this);
  return arity;
}

```

This is caught by `PREDICATE`, which calls `PL_raise_exception()` to pass the `PlTypeError` to Prolog.

The `PlFail` and `PlException` stuff works fine if the C++ code doesn’t want to do any error checking (with `try`-`catch`). But if you want a `try`-`catch` around the method call (e.g., with `q.next_solution()`), then things become more complicated because you have to know whether the underlying method can throw `PlFail` (which requires an additional check for whether there’s an exception) or `PlException`. In other words, the code becomes like this (untested):

```cpp
try
{ while (q.next_solution()) )
      <handle answer>
} catch ( PlFail& )
{ if ( PL_exception(0) )
  { <handle Prolog exception>
  } else
  } <handle next_solution() failed>
} catch ( PlException& )
{ <handle C++ exception>
}

```

I don’t want to burden every method call with an additional call to `PL_exception(0)`; but it would be good if we could simplify the error handling where we want to catch the exception and not simply pass it to Prolog.

_[This was written in a bit of a hurry; I hope it makes sense.]_

---

<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 22, 2022, 1:55pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/17 "2022-12-22T13:55:32Z")

</div>

This is why my branch changes PlCheck() to throw the real exception if one is pending. That causes some more work for the failure path and even a bit more if there is an exception, but that shouldn’t matter too much (exception recovery in C++ seems slow anyway).

The added PlCheckEx() returns the boolean value, throwing only if there is an exception. That is intended for all API wrappers that may both return logical success/failure and exceptions. PlCheck should be used for wrappers that have no logical failure path, e.g., most of the API that creates something.

---

<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 22, 2022, 6:36pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/18 "2022-12-22T18:36:16Z")

</div>

> [@jan](#):
>
> This is why my branch changes PlCheck() to throw the real exception if one is pending.

Yes, that looks reasonable (I wasn’t able to look at your “exception” branch until just now).  
However, I’d like to think about it a bit more before making this change.

> [@jan](#):
>
> exception recovery in C++ seems slow anyway

A simple benchmark showed a performance penalty of 15x to 20x between `return false` and `throw PlFail()` and the subsequent handling within `PREDICATE`. (There’s no overhead by having a try-catch if nothing is thrown.  
I presume (but haven’t measured) that the cost of the call to `PL_exception(0)` is small compared to the cost of the throw/catch, so the extra work in the failure path shouldn’t matter. The performance-obsessed programmer can still use `return PL_raise_exception(…)`.

---

<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 24, 2022, 2:05pm UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/19 "2022-12-24T14:05:03Z")

</div>

> [@jan](#):
>
> I created a branch “exceptions” with a (partial) solution to this problem

I think that `PlCheckEx()` and the modified `PlCheck()` make sense – I’ll add some tests to verify that they behave the way @mgondan1 expects (and he’s not alone – I ran into something similar but don’t follow up), plus add/fix some documentation. [Also, some minor changes to @jan’s code for efficiency, plus cover all the paths]

The issues of how best to handle the various return codes from `PlQuery::next_solution()` will need a bit more thought – it might make sense to have a second version of `next_solution()` for `PL_Q_EXT_STATUS`.

---

<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:** [January 5, 2023, 12:10am UTC](https://swi-prolog.discourse.group/t/cpp2-exceptions/6040/20 "2023-01-05T00:10:30Z")

</div>

> [@jan](#):
>
> Using `PL_Q_PASS_EXCEPTION` is probably what you want as it makes exception handling consistent with the rest of the API

The flags are a bit-wise OR, but their descriptions seem to say that they are mutually distinct (e.g., it’s not clear what `PL_Q_PASS_EXCEPTION|PL_Q_CATCH_EXCEPTION` does).

Should I specify `PL_Q_PASS_EXCEPTION` or `PL_Q_NORMAL|PL_Q_PASS_EXCEPTION`?

(With the code in the `exceptions` branch, I don’t seem to have a way of turning off the traceback message - it seems to be triggered before the `throw PlException_qid()` happens … `PL_Q_CATCH_EXCEPTION` blocks the traceback message but also seems to make the exception unavailable.)

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