diff --git a/SWI-cpp2.h b/SWI-cpp2.h index e34ea44..dd0256e 100644 --- a/SWI-cpp2.h +++ b/SWI-cpp2.h @@ -88,7 +88,32 @@ particularly integer conversions. class PlAtom; class PlTerm; class PlTermv; -void PlCheck(int rc); + + +// A pseudo-exception for quick exist on failure, for use by the unify +// methods. This is special-cased in the PREDICATE et al macros. +// Note that it is *not* a subclass of PlException. See the +// documentation for more details on how this works with returning +// Prolog failure and returning exceptions. +class PlFail +{ +public: + explicit PlFail() {} +}; + + +// Throw PlFail on failure or exception. This exception is caught by +// the PREDICATE, which simply returns false ... if the failure was +// caused by an exception, SWI-Prolog will detect that and turn the +// failure into a Prolog exception. Therefore, there is no need for +// calling PL_exception(0) and doing something different if there is +// a pending Prolog exception (to call PL_exception(0), use +// PlException_qid()). +inline void +PlCheck(int rc) +{ if ( !rc ) + throw PlFail(); +} /******************************* @@ -105,9 +130,8 @@ template class WrappedC bool not_null() const { return C_ != null; } void verify() const; // Throw exception if is_null() -public: - WrappedC() : C_(null) { } - explicit WrappedC(C_t v) : C_(v) { } + explicit WrappedC(C_t v) + : C_(v) { } WrappedC(const WrappedC&) = default; // WrappedC& operator =(const WrappedC&) = default; // deprecated/deleted in PlTerm operator bool() const = delete; // Use not_null() instead @@ -159,7 +183,8 @@ class PlStringBuffers class PlFunctor : public WrappedC { public: - PlFunctor(functor_t v = null) : WrappedC() { } + PlFunctor(functor_t v) + : WrappedC(v) { } // PlFunctor(const char*) is handled by std::string constructor explicit PlFunctor(const std::string& name, size_t arity); explicit PlFunctor(const std::wstring& name, size_t arity); @@ -168,7 +193,10 @@ class PlFunctor : public WrappedC // TODO: use PlPredicate, PlModule when implemented: predicate_t pred(module_t m) const { - return PL_pred(C_, m); + predicate_t p = PL_pred(C_, m); + if ( p == nullptr ) + throw PlFail(); + return p; } PlAtom name() const; @@ -182,8 +210,8 @@ class PlFunctor : public WrappedC class PlAtom : public WrappedC { public: - PlAtom() : WrappedC() { } // Make constructor public - explicit PlAtom(atom_t v) : WrappedC(v) { } + explicit PlAtom(atom_t v) + : WrappedC(v) { } explicit PlAtom(const std::string& text) // TODO: add encoding : WrappedC(PL_new_atom_nchars(text.size(), text.data())) { verify(); @@ -269,30 +297,6 @@ class PlAtom : public WrappedC * GENERIC PROLOG TERM * *******************************/ - -// A pseudo-exception for quick exist on failure, for use by the unify -// methods. This is special-cased in the PREDICATE et al macros. -// Note that it is *not* a subclass of PlException. -class PlFail -{ -public: - explicit PlFail() {} -}; - - -// Throw PlFail on failure or exception. This exception is caught by -// the PREDICATE, which simply returns false ... if the failure was -// caused by an exception, SWI-Prolog will detect that and turn the -// failure into a Prolog exception. Therefore, there is no need for -// calling PL_exception(0) and doing something different if there is -// a pending Prolog exception (to call PL_exception(0), use -// PlException_qid()). -inline void -PlCheck(int rc) -{ if ( !rc ) - throw PlFail(); -} - class PlTerm : public WrappedC { protected: @@ -310,7 +314,8 @@ class PlTerm : public WrappedC public: PlTerm(const PlTerm&) = default; - explicit PlTerm(const PlAtom& a) : WrappedC(PL_new_term_ref()) + explicit PlTerm(const PlAtom& a) + : WrappedC(PL_new_term_ref()) { verify(); PlCheck(PL_put_atom(C_, a.C_)); } @@ -529,13 +534,14 @@ class PlTerm_atom : public PlTerm class PlTerm_var : public PlTerm { public: - explicit PlTerm_var() : PlTerm() {} + explicit PlTerm_var() { } // PlTerm() calls Pl_new_term_ref() }; class PlTerm_term_t : public PlTerm { public: - explicit PlTerm_term_t(term_t t = null) : PlTerm(t) {} + explicit PlTerm_term_t(term_t t) + : PlTerm(t) {} }; class PlTerm_integer : public PlTerm @@ -575,7 +581,6 @@ class PlTerm_recorded : public PlTerm explicit PlTerm_recorded(record_t r) { PlCheck(PL_recorded(r, C_)); } }; - /******************************* * TERM VECTOR * *******************************/ @@ -587,21 +592,22 @@ class PlTermv term_t a0_; // A vector of term_t public: - explicit PlTermv(size_t n) + explicit PlTermv(size_t n = 0) : size_(n), - a0_(PL_new_term_refs(static_cast(n))) - { if ( ! a0_ ) + a0_(n ? PL_new_term_refs(static_cast(n)) : PlTerm::null) + { if ( size_ && a0_ == PlTerm::null ) throw PlFail(); } explicit PlTermv(size_t n, const PlTerm& t0) : size_(n), a0_(t0.C_) - { if ( ! a0_ ) + { if ( size_ && a0_ == PlTerm::null ) throw PlFail(); } term_t termv() const - { return a0_; + { // Note that a0_ can be PlTerm::null if size_ == 0 + return a0_; } size_t size() const @@ -830,6 +836,7 @@ WrappedC::verify() const inline PlFunctor::PlFunctor(const std::string& name, size_t arity) + : WrappedC(null) { PlAtom a(name); C_ = PL_new_functor(a.C_, arity); PL_unregister_atom(a.C_); @@ -838,6 +845,7 @@ PlFunctor::PlFunctor(const std::string& name, size_t arity) inline PlFunctor::PlFunctor(const std::wstring& name, size_t arity) + : WrappedC(null) { PlAtom a(name); C_ = PL_new_functor(a.C_, arity); PL_unregister_atom(a.C_); @@ -1026,7 +1034,7 @@ class PlFrame private: void verify() - { if ( !fid_ ) + { if ( fid_ == static_cast(0) ) throw PlFail(); } }; @@ -1051,16 +1059,21 @@ class PlQuery } // TODO: PlQuery(const wstring& ...) PlQuery(const std::string& name, const PlTermv& av, int flags = PL_Q_PASS_EXCEPTION) - : qid_(PL_open_query(static_cast(0), flags, - // TODO: throw if PL_predicate() returns 0 - PL_predicate(name.c_str(), static_cast(av.size()), "user"), + : qid_(PL_open_query(static_cast(0), + flags, + // TODO: throw if PL_predicate() returns 0 + PL_predicate(name.c_str(), + static_cast(av.size()), + "user"), // TODO: static_cast(0) av.termv())) { verify(); } PlQuery(const std::string& module, const std::string& name, const PlTermv& av, int flags = PL_Q_PASS_EXCEPTION) : qid_(PL_open_query(static_cast(0), flags, - // TODO: throw if PL_predicate() returns 0 - PL_predicate(name.c_str(), static_cast(av.size()), module.c_str()), + // TODO: throw if PL_predicate() returns 0 + PL_predicate(name.c_str(), + static_cast(av.size()), + module.c_str()), av.termv())) { verify(); } @@ -1098,7 +1111,7 @@ class PlQuery private: void verify() - { if ( !qid_ ) + { if ( qid_ == static_cast(0) ) throw PlFail(); } }; @@ -1314,7 +1327,7 @@ PlCompound::PlCompound(const wchar_t *text) inline PlCompound::PlCompound(const std::string& text, PlEncoding enc) { term_t t = PL_new_term_ref(); - if ( ! t ) + if ( t == PlTerm::null ) throw PlFail(); // TODO: PL_put_term_from_chars() should take an unsigned int flags diff --git a/test_cpp.cpp b/test_cpp.cpp index cd616d9..0fbe69a 100644 --- a/test_cpp.cpp +++ b/test_cpp.cpp @@ -189,6 +189,13 @@ PREDICATE(hello_query, 2) return true; } +PREDICATE(call_cut, 1) +{ PlQuery q(A1.as_string(), PlTermv()); + PlCheck(q.next_solution()); + q.cut(); + return true; +} + PREDICATE(hello_call, 1) { PlCheck(PlCall(A1)); return true; @@ -500,7 +507,7 @@ PREDICATE(ensure_PlTerm_forward_declarations_are_implemented, 0) PlTerm_atom p_atom4(std::string("abc")); PlTerm_atom p_atom5(std::wstring(L"世界")); PlTerm_term_t t_t(PL_new_term_ref()); - PlTerm_term_t t_null; // null + PlTerm_term_t t_null(PlTerm::null); PlTerm_integer t_int1(std::numeric_limits::max()); PlTerm_integer t_int1b(std::numeric_limits::min()); PlTerm_integer t_int2(std::numeric_limits::max()); @@ -524,7 +531,7 @@ PREDICATE(ensure_PlTerm_forward_declarations_are_implemented, 0) PlAtom atom3(std::string("atom3")); PlAtom atom4(std::wstring(L"原子4")); PlAtom a5a(t_atom1.as_atom()); - PlAtom atom_null; + PlAtom atom_null(PlAtom::null); // The following are unsafe (the as_string() is deleted in the statement): // const char * x01 = t_var.as_string().c_str(); // const wchar_t *x01a = t_var.as_wstring().c_str(); @@ -990,7 +997,7 @@ PREDICATE(cpp_options, 3) int quoted = false; size_t length = 10; PlTerm_var callback; - PlAtom token; + PlAtom token(PlAtom::null); const char *descr = ""; bool opt_all_v = opt_all.as_bool(); int flags = opt_all_v ? OPT_ALL : 0; diff --git a/test_cpp.pl b/test_cpp.pl index 8ea46dd..da880b4 100644 --- a/test_cpp.pl +++ b/test_cpp.pl @@ -85,8 +85,9 @@ :- if(\+current_prolog_flag(windows, true)). % The C++ atom_to_string/2 takes the raw bytes from atom and creates % a byte string. This doesn't work in Windows as we use a different -% representation. This is dubious anyway. Delete entirely fix when -% the C++ interface properly supports encodings. +% representation. This is dubious anyway. +% TODO: Delete entire fix when the C++ interface properly supports encodings. +% See: https://swi-prolog.discourse.group/t/ann-swi-prolog-9-1-0/5964/3 test(as_string, S == "ä¸\u0096ç\u0095\u008Cå\u009B\u009B") :- atom_to_string(世界四, S). test(as_string, S = "hello(ä¸\u0096ç\u0095\u008Cå\u009B\u009B)") :- @@ -118,6 +119,16 @@ test(hello_0, Out == "hello world\n") :- with_output_to(string(Out), hello). +call_cut_test :- + setup_call_cleanup(true, + between(1, 5, _X), + atom_codes(_,_)). + +test(call_cut, error(existence_error(procedure,call_cut_test/0))) :- + % This tests that an error in ~PlQuery() is handled properly + % See discussion: https://github.com/SWI-Prolog/packages-cpp/pull/27 + call_cut("call_cut_test"). + test(term_1, Term = hello(world)) :- term(Term).